2017-01-25 23:58:14 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/* 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 "LocaleService.h"
|
2017-02-05 20:29:39 +00:00
|
|
|
|
2017-02-25 01:23:39 +00:00
|
|
|
#include <algorithm> // find_if()
|
2017-01-25 23:58:14 +00:00
|
|
|
#include "mozilla/ClearOnShutdown.h"
|
2017-09-23 11:46:28 +00:00
|
|
|
#include "mozilla/Omnijar.h"
|
2017-03-06 21:24:45 +00:00
|
|
|
#include "mozilla/Preferences.h"
|
2017-09-23 11:46:28 +00:00
|
|
|
#include "mozilla/Services.h"
|
2017-03-06 21:24:45 +00:00
|
|
|
#include "mozilla/intl/OSPreferences.h"
|
2017-01-25 23:58:14 +00:00
|
|
|
#include "nsIObserverService.h"
|
|
|
|
#include "nsIToolkitChromeRegistry.h"
|
2017-09-23 11:46:28 +00:00
|
|
|
#include "nsStringEnumerator.h"
|
2017-03-14 22:28:47 +00:00
|
|
|
#include "nsXULAppAPI.h"
|
2017-09-23 11:46:28 +00:00
|
|
|
#include "nsZipArchive.h"
|
2017-01-25 23:58:14 +00:00
|
|
|
|
2017-02-25 01:23:39 +00:00
|
|
|
#include "unicode/uloc.h"
|
|
|
|
|
2017-03-06 21:24:45 +00:00
|
|
|
#define MATCH_OS_LOCALE_PREF "intl.locale.matchOS"
|
|
|
|
#define SELECTED_LOCALE_PREF "general.useragent.locale"
|
|
|
|
|
2017-03-30 23:30:54 +00:00
|
|
|
//XXX: This pref is used only by Android and we use it to emulate
|
|
|
|
// retrieving OS locale until we get proper hook into JNI in bug 1337078.
|
|
|
|
#define ANDROID_OS_LOCALE_PREF "intl.locale.os"
|
|
|
|
|
2017-03-06 21:24:45 +00:00
|
|
|
static const char* kObservedPrefs[] = {
|
|
|
|
MATCH_OS_LOCALE_PREF,
|
|
|
|
SELECTED_LOCALE_PREF,
|
2017-03-30 23:30:54 +00:00
|
|
|
ANDROID_OS_LOCALE_PREF,
|
2017-03-06 21:24:45 +00:00
|
|
|
nullptr
|
|
|
|
};
|
|
|
|
|
2017-01-25 23:58:14 +00:00
|
|
|
using namespace mozilla::intl;
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
using namespace mozilla;
|
2017-01-25 23:58:14 +00:00
|
|
|
|
2017-09-09 00:51:43 +00:00
|
|
|
NS_IMPL_ISUPPORTS(LocaleService, mozILocaleService, nsIObserver,
|
|
|
|
nsISupportsWeakReference)
|
2017-02-05 20:29:39 +00:00
|
|
|
|
|
|
|
mozilla::StaticRefPtr<LocaleService> LocaleService::sInstance;
|
|
|
|
|
2017-03-13 15:31:43 +00:00
|
|
|
/**
|
|
|
|
* This function transforms a canonical Mozilla Language Tag, into it's
|
|
|
|
* BCP47 compilant form.
|
|
|
|
*
|
|
|
|
* Example: "ja-JP-mac" -> "ja-JP-x-lvariant-mac"
|
|
|
|
*
|
|
|
|
* The BCP47 form should be used for all calls to ICU/Intl APIs.
|
|
|
|
* The canonical form is used for all internal operations.
|
|
|
|
*/
|
2017-03-14 19:35:06 +00:00
|
|
|
static void
|
|
|
|
SanitizeForBCP47(nsACString& aLocale)
|
2017-03-13 15:31:43 +00:00
|
|
|
{
|
|
|
|
// Currently, the only locale code we use that's not BCP47-conformant is
|
|
|
|
// "ja-JP-mac" on OS X, but let's try to be more general than just
|
|
|
|
// hard-coding that here.
|
|
|
|
const int32_t LANG_TAG_CAPACITY = 128;
|
|
|
|
char langTag[LANG_TAG_CAPACITY];
|
|
|
|
nsAutoCString locale(aLocale);
|
|
|
|
UErrorCode err = U_ZERO_ERROR;
|
|
|
|
// This is a fail-safe method that will set langTag to "und" if it cannot
|
|
|
|
// match any part of the input locale code.
|
|
|
|
int32_t len = uloc_toLanguageTag(locale.get(), langTag, LANG_TAG_CAPACITY,
|
|
|
|
false, &err);
|
|
|
|
if (U_SUCCESS(err) && len > 0) {
|
|
|
|
aLocale.Assign(langTag, len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
static bool
|
|
|
|
ReadRequestedLocales(nsTArray<nsCString>& aRetVal)
|
|
|
|
{
|
|
|
|
nsAutoCString locale;
|
|
|
|
|
|
|
|
// First, we'll try to check if the user has `matchOS` pref selected
|
|
|
|
bool matchOSLocale = Preferences::GetBool(MATCH_OS_LOCALE_PREF);
|
|
|
|
|
|
|
|
if (matchOSLocale) {
|
|
|
|
// If he has, we'll pick the locale from the system
|
|
|
|
if (OSPreferences::GetInstance()->GetSystemLocales(aRetVal)) {
|
|
|
|
// If we succeeded, return.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, we'll try to get the requested locale from the prefs.
|
2017-07-31 00:52:51 +00:00
|
|
|
if (!NS_SUCCEEDED(Preferences::GetCString(SELECTED_LOCALE_PREF, locale))) {
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// At the moment we just take a single locale, but in the future
|
|
|
|
// we'll want to allow user to specify a list of requested locales.
|
|
|
|
aRetVal.AppendElement(locale);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
ReadAvailableLocales(nsTArray<nsCString>& aRetVal)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIToolkitChromeRegistry> cr =
|
|
|
|
mozilla::services::GetToolkitChromeRegistryService();
|
|
|
|
if (!cr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIUTF8StringEnumerator> localesEnum;
|
|
|
|
|
|
|
|
nsresult rv =
|
|
|
|
cr->GetLocalesForPackage(NS_LITERAL_CSTRING("global"), getter_AddRefs(localesEnum));
|
|
|
|
if (!NS_SUCCEEDED(rv)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool more;
|
|
|
|
while (NS_SUCCEEDED(rv = localesEnum->HasMore(&more)) && more) {
|
|
|
|
nsAutoCString localeStr;
|
|
|
|
rv = localesEnum->GetNext(localeStr);
|
|
|
|
if (!NS_SUCCEEDED(rv)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
aRetVal.AppendElement(localeStr);
|
|
|
|
}
|
|
|
|
return !aRetVal.IsEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
LocaleService::LocaleService(bool aIsServer)
|
|
|
|
:mIsServer(aIsServer)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-01-25 23:58:14 +00:00
|
|
|
/**
|
|
|
|
* This function performs the actual language negotiation for the API.
|
|
|
|
*
|
|
|
|
* Currently it collects the locale ID used by nsChromeRegistry and
|
|
|
|
* adds hardcoded "en-US" locale as a fallback.
|
|
|
|
*/
|
2017-03-14 22:28:47 +00:00
|
|
|
void
|
|
|
|
LocaleService::NegotiateAppLocales(nsTArray<nsCString>& aRetVal)
|
2017-01-25 23:58:14 +00:00
|
|
|
{
|
2017-03-14 22:28:47 +00:00
|
|
|
nsAutoCString defaultLocale;
|
|
|
|
GetDefaultLocale(defaultLocale);
|
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
if (mIsServer) {
|
2017-03-14 22:28:47 +00:00
|
|
|
AutoTArray<nsCString, 100> availableLocales;
|
|
|
|
AutoTArray<nsCString, 10> requestedLocales;
|
|
|
|
GetAvailableLocales(availableLocales);
|
|
|
|
GetRequestedLocales(requestedLocales);
|
|
|
|
|
|
|
|
NegotiateLanguages(requestedLocales, availableLocales, defaultLocale,
|
|
|
|
LangNegStrategy::Filtering, aRetVal);
|
|
|
|
} else {
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
// In content process, we will not do any language negotiation.
|
|
|
|
// Instead, the language is set manually by SetAppLocales.
|
|
|
|
//
|
|
|
|
// If this method has been called, it means that we did not fire
|
|
|
|
// SetAppLocales yet (happens during initialization).
|
|
|
|
// In that case, all we can do is return the default locale.
|
|
|
|
// Once SetAppLocales will be called later, it'll fire an event
|
|
|
|
// allowing callers to update the locale.
|
|
|
|
aRetVal.AppendElement(defaultLocale);
|
2017-01-25 23:58:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-05 20:29:39 +00:00
|
|
|
LocaleService*
|
|
|
|
LocaleService::GetInstance()
|
2017-01-25 23:58:14 +00:00
|
|
|
{
|
|
|
|
if (!sInstance) {
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
sInstance = new LocaleService(XRE_IsParentProcess());
|
2017-03-06 21:24:45 +00:00
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
if (sInstance->IsServer()) {
|
|
|
|
// We're going to observe for requested languages changes which come
|
|
|
|
// from prefs.
|
2017-09-09 00:51:43 +00:00
|
|
|
DebugOnly<nsresult> rv = Preferences::AddWeakObservers(sInstance, kObservedPrefs);
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv), "Adding observers failed.");
|
|
|
|
}
|
2017-01-25 23:58:14 +00:00
|
|
|
ClearOnShutdown(&sInstance);
|
|
|
|
}
|
|
|
|
return sInstance;
|
|
|
|
}
|
|
|
|
|
2017-03-06 21:24:45 +00:00
|
|
|
LocaleService::~LocaleService()
|
|
|
|
{
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
if (mIsServer) {
|
2017-09-09 00:51:43 +00:00
|
|
|
Preferences::RemoveObservers(this, kObservedPrefs);
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
}
|
2017-03-06 21:24:45 +00:00
|
|
|
}
|
|
|
|
|
2017-01-25 23:58:14 +00:00
|
|
|
void
|
2017-03-13 15:31:43 +00:00
|
|
|
LocaleService::GetAppLocalesAsLangTags(nsTArray<nsCString>& aRetVal)
|
2017-01-25 23:58:14 +00:00
|
|
|
{
|
|
|
|
if (mAppLocales.IsEmpty()) {
|
2017-03-14 22:28:47 +00:00
|
|
|
NegotiateAppLocales(mAppLocales);
|
2017-01-25 23:58:14 +00:00
|
|
|
}
|
|
|
|
aRetVal = mAppLocales;
|
|
|
|
}
|
|
|
|
|
2017-03-13 15:31:43 +00:00
|
|
|
void
|
|
|
|
LocaleService::GetAppLocalesAsBCP47(nsTArray<nsCString>& aRetVal)
|
|
|
|
{
|
|
|
|
if (mAppLocales.IsEmpty()) {
|
2017-03-14 22:28:47 +00:00
|
|
|
NegotiateAppLocales(mAppLocales);
|
2017-03-13 15:31:43 +00:00
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < mAppLocales.Length(); i++) {
|
|
|
|
nsAutoCString locale(mAppLocales[i]);
|
|
|
|
SanitizeForBCP47(locale);
|
|
|
|
aRetVal.AppendElement(locale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-08 20:54:04 +00:00
|
|
|
void
|
|
|
|
LocaleService::GetRegionalPrefsLocales(nsTArray<nsCString>& aRetVal)
|
|
|
|
{
|
|
|
|
bool useOSLocales = Preferences::GetBool("intl.regional_prefs.use_os_locales", false);
|
|
|
|
|
2017-07-13 17:40:40 +00:00
|
|
|
if (useOSLocales && OSPreferences::GetInstance()->GetRegionalPrefsLocales(aRetVal)) {
|
2017-07-08 20:54:04 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetAppLocalesAsBCP47(aRetVal);
|
|
|
|
}
|
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
void
|
|
|
|
LocaleService::AssignAppLocales(const nsTArray<nsCString>& aAppLocales)
|
2017-03-06 21:24:45 +00:00
|
|
|
{
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
MOZ_ASSERT(!mIsServer, "This should only be called for LocaleService in client mode.");
|
2017-03-06 21:24:45 +00:00
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
mAppLocales = aAppLocales;
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
|
|
if (obs) {
|
|
|
|
obs->NotifyObservers(nullptr, "intl:app-locales-changed", nullptr);
|
|
|
|
}
|
|
|
|
}
|
2017-03-06 21:24:45 +00:00
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
void
|
|
|
|
LocaleService::AssignRequestedLocales(const nsTArray<nsCString>& aRequestedLocales)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mIsServer, "This should only be called for LocaleService in client mode.");
|
|
|
|
|
|
|
|
mRequestedLocales = aRequestedLocales;
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
|
|
if (obs) {
|
|
|
|
obs->NotifyObservers(nullptr, "intl:requested-locales-changed", nullptr);
|
2017-03-06 21:24:45 +00:00
|
|
|
}
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
}
|
2017-03-06 21:24:45 +00:00
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
bool
|
|
|
|
LocaleService::GetRequestedLocales(nsTArray<nsCString>& aRetVal)
|
|
|
|
{
|
|
|
|
if (mRequestedLocales.IsEmpty()) {
|
|
|
|
ReadRequestedLocales(mRequestedLocales);
|
2017-03-06 21:24:45 +00:00
|
|
|
}
|
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
aRetVal = mRequestedLocales;
|
2017-03-06 21:24:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-14 00:33:24 +00:00
|
|
|
bool
|
|
|
|
LocaleService::GetAvailableLocales(nsTArray<nsCString>& aRetVal)
|
|
|
|
{
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
if (mAvailableLocales.IsEmpty()) {
|
|
|
|
ReadAvailableLocales(mAvailableLocales);
|
|
|
|
}
|
2017-03-14 00:33:24 +00:00
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
aRetVal = mAvailableLocales;
|
|
|
|
return true;
|
|
|
|
}
|
2017-03-14 00:33:24 +00:00
|
|
|
|
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
void
|
2017-10-10 20:05:03 +00:00
|
|
|
LocaleService::OnAvailableLocalesChanged()
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(mIsServer, "This should only be called in the server mode.");
|
|
|
|
mAvailableLocales.Clear();
|
|
|
|
// In the future we may want to trigger here intl:available-locales-changed
|
2017-10-10 20:05:03 +00:00
|
|
|
OnLocalesChanged();
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
}
|
2017-03-14 00:33:24 +00:00
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
void
|
2017-10-10 20:05:03 +00:00
|
|
|
LocaleService::OnRequestedLocalesChanged()
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(mIsServer, "This should only be called in the server mode.");
|
|
|
|
|
|
|
|
nsTArray<nsCString> newLocales;
|
|
|
|
ReadRequestedLocales(newLocales);
|
|
|
|
|
|
|
|
if (mRequestedLocales != newLocales) {
|
|
|
|
mRequestedLocales = Move(newLocales);
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
|
|
if (obs) {
|
|
|
|
obs->NotifyObservers(nullptr, "intl:requested-locales-changed", nullptr);
|
|
|
|
}
|
2017-10-10 20:05:03 +00:00
|
|
|
OnLocalesChanged();
|
2017-03-14 00:33:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-25 23:58:14 +00:00
|
|
|
void
|
2017-10-10 20:05:03 +00:00
|
|
|
LocaleService::OnLocalesChanged()
|
2017-01-25 23:58:14 +00:00
|
|
|
{
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
MOZ_ASSERT(mIsServer, "This should only be called in the server mode.");
|
|
|
|
|
2017-03-14 22:28:47 +00:00
|
|
|
// if mAppLocales has not been initialized yet, just return
|
|
|
|
if (mAppLocales.IsEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-25 23:58:14 +00:00
|
|
|
nsTArray<nsCString> newLocales;
|
2017-03-14 22:28:47 +00:00
|
|
|
NegotiateAppLocales(newLocales);
|
2017-01-25 23:58:14 +00:00
|
|
|
|
|
|
|
if (mAppLocales != newLocales) {
|
|
|
|
mAppLocales = Move(newLocales);
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
|
|
if (obs) {
|
|
|
|
obs->NotifyObservers(nullptr, "intl:app-locales-changed", nullptr);
|
2017-03-14 22:28:47 +00:00
|
|
|
|
|
|
|
// Deprecated, please use `intl:app-locales-changed`.
|
|
|
|
// Kept for now for compatibility reasons
|
|
|
|
obs->NotifyObservers(nullptr, "selected-locale-has-changed", nullptr);
|
2017-01-25 23:58:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-05 20:29:39 +00:00
|
|
|
|
2017-02-25 01:23:39 +00:00
|
|
|
// After trying each step of the negotiation algorithm for each requested locale,
|
|
|
|
// if a match was found we use this macro to decide whether to return immediately,
|
|
|
|
// skip to the next requested locale, or continue searching for additional matches,
|
|
|
|
// according to the desired negotiation strategy.
|
|
|
|
#define HANDLE_STRATEGY \
|
|
|
|
switch (aStrategy) { \
|
|
|
|
case LangNegStrategy::Lookup: \
|
|
|
|
return; \
|
|
|
|
case LangNegStrategy::Matching: \
|
|
|
|
continue; \
|
|
|
|
case LangNegStrategy::Filtering: \
|
|
|
|
break; \
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the raw algorithm for language negotiation based roughly
|
|
|
|
* on RFC4647 language filtering, with changes from LDML language matching.
|
|
|
|
*
|
Bug 1400006 - Extend language negotiation in LocaleService to support looking for the best likelySubtag for the locale with region stripped. r=Pike
Add additional logic to our language negotation to do apply likelySubtags when a direct match is not available.
Currently, if the user specifies the locale with region, and we do not have a direct for that region, we pick all locales for the same language and other regions in no order.
The example of where it returns suboptimal results:
1) Requested locale "en-CA"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-ZA", "en-GB", "en-US"]
This would not happen, if the user requested a generic "de", "en" etc.:
1) Requested locale "en"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-US", "en-ZA", "en-GB"]
because after not finding a direct match, we would use likelySubtags to extend "en" to "en-Latn-US" and then find the priority match in "en-US".
This patch extends this logic to "en-US" or "de-LU" by adding a step which strips the region tag and then applies likelySubtag on the result.
This means that in absence of direct match the following fallbacks would happen:
"de-LU" -> "de-DE"
"es-CL" -> "es-ES"
"en-CA" -> "en-US"
This does not affect languages that use multiple scripts, so ar, sr and zh are not affected.
MozReview-Commit-ID: BR1WrgXSf6a
--HG--
extra : rebase_source : abc205c4f993680ab0cd0c8b8c016543d5462d01
2017-09-14 22:21:33 +00:00
|
|
|
* The exact algorithm is custom, and consists of a 6 level strategy:
|
2017-02-25 01:23:39 +00:00
|
|
|
*
|
|
|
|
* 1) Attempt to find an exact match for each requested locale in available
|
|
|
|
* locales.
|
|
|
|
* Example: ['en-US'] * ['en-US'] = ['en-US']
|
|
|
|
*
|
|
|
|
* 2) Attempt to match a requested locale to an available locale treated
|
|
|
|
* as a locale range.
|
|
|
|
* Example: ['en-US'] * ['en'] = ['en']
|
|
|
|
* ^^
|
|
|
|
* |-- becomes 'en-*-*-*'
|
|
|
|
*
|
|
|
|
* 3) Attempt to use the maximized version of the requested locale, to
|
|
|
|
* find the best match in available locales.
|
|
|
|
* Example: ['en'] * ['en-GB', 'en-US'] = ['en-US']
|
|
|
|
* ^^
|
|
|
|
* |-- ICU likelySubtags expands it to 'en-Latn-US'
|
|
|
|
*
|
|
|
|
* 4) Attempt to look up for a different variant of the same locale.
|
|
|
|
* Example: ['ja-JP-win'] * ['ja-JP-mac'] = ['ja-JP-mac']
|
|
|
|
* ^^^^^^^^^
|
|
|
|
* |----------- replace variant with range: 'ja-JP-*'
|
|
|
|
*
|
Bug 1400006 - Extend language negotiation in LocaleService to support looking for the best likelySubtag for the locale with region stripped. r=Pike
Add additional logic to our language negotation to do apply likelySubtags when a direct match is not available.
Currently, if the user specifies the locale with region, and we do not have a direct for that region, we pick all locales for the same language and other regions in no order.
The example of where it returns suboptimal results:
1) Requested locale "en-CA"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-ZA", "en-GB", "en-US"]
This would not happen, if the user requested a generic "de", "en" etc.:
1) Requested locale "en"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-US", "en-ZA", "en-GB"]
because after not finding a direct match, we would use likelySubtags to extend "en" to "en-Latn-US" and then find the priority match in "en-US".
This patch extends this logic to "en-US" or "de-LU" by adding a step which strips the region tag and then applies likelySubtag on the result.
This means that in absence of direct match the following fallbacks would happen:
"de-LU" -> "de-DE"
"es-CL" -> "es-ES"
"en-CA" -> "en-US"
This does not affect languages that use multiple scripts, so ar, sr and zh are not affected.
MozReview-Commit-ID: BR1WrgXSf6a
--HG--
extra : rebase_source : abc205c4f993680ab0cd0c8b8c016543d5462d01
2017-09-14 22:21:33 +00:00
|
|
|
* 5) Attempt to look up for a maximized version of the requested locale,
|
|
|
|
* stripped of the region code.
|
|
|
|
* Example: ['en-CA'] * ['en-ZA', 'en-US'] = ['en-US', 'en-ZA']
|
|
|
|
* ^^^^^
|
|
|
|
* |----------- look for likelySubtag of 'en': 'en-Latn-US'
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* 6) Attempt to look up for a different region of the same locale.
|
2017-02-25 01:23:39 +00:00
|
|
|
* Example: ['en-GB'] * ['en-AU'] = ['en-AU']
|
|
|
|
* ^^^^^
|
|
|
|
* |----- replace region with range: 'en-*'
|
|
|
|
*
|
|
|
|
* It uses one of the strategies described in LocaleService.h.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
LocaleService::FilterMatches(const nsTArray<nsCString>& aRequested,
|
|
|
|
const nsTArray<nsCString>& aAvailable,
|
|
|
|
LangNegStrategy aStrategy,
|
|
|
|
nsTArray<nsCString>& aRetVal)
|
|
|
|
{
|
|
|
|
// Local copy of the list of available locales, in Locale form for flexible
|
|
|
|
// matching. We will remove entries from this list as they get appended to
|
|
|
|
// aRetVal, so that no available locale will be found more than once.
|
|
|
|
AutoTArray<Locale, 100> availLocales;
|
|
|
|
for (auto& avail : aAvailable) {
|
|
|
|
availLocales.AppendElement(Locale(avail, true));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper to erase an entry from availLocales once we have copied it to
|
|
|
|
// the result list. Returns an iterator pointing to the entry that was
|
|
|
|
// immediately after the one that was erased (or availLocales.end() if
|
|
|
|
// the target was the last in the array).
|
|
|
|
auto eraseFromAvail = [&](nsTArray<Locale>::iterator aIter) {
|
|
|
|
nsTArray<Locale>::size_type index = aIter - availLocales.begin();
|
|
|
|
availLocales.RemoveElementAt(index);
|
|
|
|
return availLocales.begin() + index;
|
|
|
|
};
|
|
|
|
|
|
|
|
for (auto& requested : aRequested) {
|
|
|
|
|
|
|
|
// 1) Try to find a simple (case-insensitive) string match for the request.
|
|
|
|
auto matchesExactly = [&](const Locale& aLoc) {
|
|
|
|
return requested.Equals(aLoc.AsString(),
|
|
|
|
nsCaseInsensitiveCStringComparator());
|
|
|
|
};
|
|
|
|
auto match = std::find_if(availLocales.begin(), availLocales.end(),
|
|
|
|
matchesExactly);
|
|
|
|
if (match != availLocales.end()) {
|
|
|
|
aRetVal.AppendElement(match->AsString());
|
|
|
|
eraseFromAvail(match);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aRetVal.IsEmpty()) {
|
|
|
|
HANDLE_STRATEGY;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2) Try to match against the available locales treated as ranges.
|
|
|
|
auto findRangeMatches = [&](const Locale& aReq) {
|
|
|
|
auto matchesRange = [&](const Locale& aLoc) {
|
|
|
|
return aReq.Matches(aLoc);
|
|
|
|
};
|
|
|
|
bool foundMatch = false;
|
|
|
|
auto match = availLocales.begin();
|
|
|
|
while ((match = std::find_if(match, availLocales.end(),
|
|
|
|
matchesRange)) != availLocales.end()) {
|
|
|
|
aRetVal.AppendElement(match->AsString());
|
|
|
|
match = eraseFromAvail(match);
|
|
|
|
foundMatch = true;
|
|
|
|
if (aStrategy != LangNegStrategy::Filtering) {
|
|
|
|
return true; // we only want the first match
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return foundMatch;
|
|
|
|
};
|
|
|
|
|
|
|
|
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)) {
|
|
|
|
HANDLE_STRATEGY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 4) Try to match against a variant as a range
|
|
|
|
requestedLocale.SetVariantRange();
|
|
|
|
if (findRangeMatches(requestedLocale)) {
|
|
|
|
HANDLE_STRATEGY;
|
|
|
|
}
|
|
|
|
|
Bug 1400006 - Extend language negotiation in LocaleService to support looking for the best likelySubtag for the locale with region stripped. r=Pike
Add additional logic to our language negotation to do apply likelySubtags when a direct match is not available.
Currently, if the user specifies the locale with region, and we do not have a direct for that region, we pick all locales for the same language and other regions in no order.
The example of where it returns suboptimal results:
1) Requested locale "en-CA"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-ZA", "en-GB", "en-US"]
This would not happen, if the user requested a generic "de", "en" etc.:
1) Requested locale "en"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-US", "en-ZA", "en-GB"]
because after not finding a direct match, we would use likelySubtags to extend "en" to "en-Latn-US" and then find the priority match in "en-US".
This patch extends this logic to "en-US" or "de-LU" by adding a step which strips the region tag and then applies likelySubtag on the result.
This means that in absence of direct match the following fallbacks would happen:
"de-LU" -> "de-DE"
"es-CL" -> "es-ES"
"en-CA" -> "en-US"
This does not affect languages that use multiple scripts, so ar, sr and zh are not affected.
MozReview-Commit-ID: BR1WrgXSf6a
--HG--
extra : rebase_source : abc205c4f993680ab0cd0c8b8c016543d5462d01
2017-09-14 22:21:33 +00:00
|
|
|
// 5) Try to match against the likely subtag without region
|
|
|
|
if (requestedLocale.AddLikelySubtagsWithoutRegion()) {
|
|
|
|
if (findRangeMatches(requestedLocale)) {
|
|
|
|
HANDLE_STRATEGY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 6) Try to match against a region as a range
|
2017-02-25 01:23:39 +00:00
|
|
|
requestedLocale.SetRegionRange();
|
|
|
|
if (findRangeMatches(requestedLocale)) {
|
|
|
|
HANDLE_STRATEGY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
LocaleService::NegotiateLanguages(const nsTArray<nsCString>& aRequested,
|
|
|
|
const nsTArray<nsCString>& aAvailable,
|
|
|
|
const nsACString& aDefaultLocale,
|
|
|
|
LangNegStrategy aStrategy,
|
|
|
|
nsTArray<nsCString>& aRetVal)
|
|
|
|
{
|
|
|
|
// If the strategy is Lookup, we require the defaultLocale to be set.
|
|
|
|
if (aStrategy == LangNegStrategy::Lookup && aDefaultLocale.IsEmpty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
FilterMatches(aRequested, aAvailable, aStrategy, aRetVal);
|
|
|
|
|
|
|
|
if (aStrategy == LangNegStrategy::Lookup) {
|
|
|
|
if (aRetVal.Length() == 0) {
|
|
|
|
// If the strategy is Lookup and Filtering returned no matches, use
|
|
|
|
// the default locale.
|
|
|
|
aRetVal.AppendElement(aDefaultLocale);
|
|
|
|
}
|
|
|
|
} else if (!aDefaultLocale.IsEmpty() && !aRetVal.Contains(aDefaultLocale)) {
|
|
|
|
// If it's not a Lookup strategy, add the default locale only if it's
|
|
|
|
// set and it's not in the results already.
|
|
|
|
aRetVal.AppendElement(aDefaultLocale);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-03-14 19:35:06 +00:00
|
|
|
bool
|
|
|
|
LocaleService::IsAppLocaleRTL()
|
|
|
|
{
|
|
|
|
nsAutoCString locale;
|
|
|
|
GetAppLocaleAsBCP47(locale);
|
|
|
|
|
|
|
|
int pref = Preferences::GetInt("intl.uidirection", -1);
|
|
|
|
if (pref >= 0) {
|
|
|
|
return (pref > 0);
|
|
|
|
}
|
|
|
|
return uloc_isRightToLeft(locale.get());
|
|
|
|
}
|
|
|
|
|
2017-03-06 21:24:45 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::Observe(nsISupports *aSubject, const char *aTopic,
|
|
|
|
const char16_t *aData)
|
|
|
|
{
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
MOZ_ASSERT(mIsServer, "This should only be called in the server mode.");
|
|
|
|
|
2017-10-10 20:05:58 +00:00
|
|
|
NS_ConvertUTF16toUTF8 pref(aData);
|
2017-10-05 16:43:38 +00:00
|
|
|
|
2017-10-10 20:05:58 +00:00
|
|
|
// This is a temporary solution until we get bug 1337078 landed.
|
|
|
|
if (pref.EqualsLiteral(ANDROID_OS_LOCALE_PREF)) {
|
|
|
|
OSPreferences::GetInstance()->Refresh();
|
|
|
|
}
|
|
|
|
// At the moment the only thing we're observing are settings indicating
|
|
|
|
// user requested locales.
|
|
|
|
if (pref.EqualsLiteral(MATCH_OS_LOCALE_PREF) ||
|
|
|
|
pref.EqualsLiteral(SELECTED_LOCALE_PREF) ||
|
|
|
|
pref.EqualsLiteral(ANDROID_OS_LOCALE_PREF)) {
|
|
|
|
OnRequestedLocalesChanged();
|
2017-03-06 21:24:45 +00:00
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2017-02-25 01:23:39 +00:00
|
|
|
|
2017-03-03 00:39:17 +00:00
|
|
|
bool
|
|
|
|
LocaleService::LanguagesMatch(const nsCString& aRequested,
|
|
|
|
const nsCString& aAvailable)
|
|
|
|
{
|
|
|
|
return Locale(aRequested, true).LanguageMatches(Locale(aAvailable, true));
|
|
|
|
}
|
|
|
|
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
|
|
|
|
bool
|
|
|
|
LocaleService::IsServer()
|
|
|
|
{
|
|
|
|
return mIsServer;
|
|
|
|
}
|
|
|
|
|
2017-02-05 20:29:39 +00:00
|
|
|
/**
|
|
|
|
* mozILocaleService methods
|
|
|
|
*/
|
2017-03-06 21:24:45 +00:00
|
|
|
|
|
|
|
static char**
|
|
|
|
CreateOutArray(const nsTArray<nsCString>& aArray)
|
|
|
|
{
|
|
|
|
uint32_t n = aArray.Length();
|
|
|
|
char** result = static_cast<char**>(moz_xmalloc(n * sizeof(char*)));
|
|
|
|
for (uint32_t i = 0; i < n; i++) {
|
|
|
|
result[i] = moz_xstrdup(aArray[i].get());
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-03-14 20:04:59 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::GetDefaultLocale(nsACString& aRetVal)
|
|
|
|
{
|
2017-09-23 11:46:28 +00:00
|
|
|
// We don't allow this to change during a session (it's set at build/package
|
|
|
|
// time), so we cache the result the first time we're called.
|
|
|
|
if (mDefaultLocale.IsEmpty()) {
|
|
|
|
// Try to get the package locale from update.locale in omnijar. If the
|
|
|
|
// update.locale file is not found, item.len will remain 0 and we'll
|
|
|
|
// just use our hard-coded default below.
|
|
|
|
// (We could also search for an update.locale file in the GRE resources
|
|
|
|
// directory, to support non-packaged builds, but that seems like a lot
|
|
|
|
// of extra code for what is probably not an important use case.)
|
|
|
|
RefPtr<nsZipArchive> zip = Omnijar::GetReader(Omnijar::GRE);
|
|
|
|
if (zip) {
|
|
|
|
nsZipItemPtr<char> item(zip, "update.locale");
|
|
|
|
size_t len = item.Length();
|
|
|
|
// Ignore any trailing spaces, newlines, etc.
|
|
|
|
while (len > 0 && item.Buffer()[len - 1] <= ' ') {
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
mDefaultLocale.Assign(item.Buffer(), len);
|
|
|
|
}
|
|
|
|
// Hard-coded fallback, e.g. for non-packaged developer builds.
|
|
|
|
// XXX Is there any reason to make this a compile-time #define that
|
|
|
|
// can be set via configure or something?
|
|
|
|
if (mDefaultLocale.IsEmpty()) {
|
|
|
|
mDefaultLocale.AssignLiteral("en-US");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aRetVal = mDefaultLocale;
|
2017-03-14 20:04:59 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-02-05 20:29:39 +00:00
|
|
|
NS_IMETHODIMP
|
2017-03-13 15:31:43 +00:00
|
|
|
LocaleService::GetAppLocalesAsLangTags(uint32_t* aCount, char*** aOutArray)
|
2017-02-05 20:29:39 +00:00
|
|
|
{
|
|
|
|
if (mAppLocales.IsEmpty()) {
|
2017-03-14 22:28:47 +00:00
|
|
|
NegotiateAppLocales(mAppLocales);
|
2017-02-05 20:29:39 +00:00
|
|
|
}
|
|
|
|
|
2017-02-20 21:14:25 +00:00
|
|
|
*aCount = mAppLocales.Length();
|
2017-03-06 21:24:45 +00:00
|
|
|
*aOutArray = CreateOutArray(mAppLocales);
|
2017-02-05 20:29:39 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2017-03-13 15:31:43 +00:00
|
|
|
LocaleService::GetAppLocalesAsBCP47(uint32_t* aCount, char*** aOutArray)
|
|
|
|
{
|
|
|
|
AutoTArray<nsCString, 32> locales;
|
|
|
|
GetAppLocalesAsBCP47(locales);
|
|
|
|
|
|
|
|
*aCount = locales.Length();
|
|
|
|
*aOutArray = CreateOutArray(locales);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::GetAppLocaleAsLangTag(nsACString& aRetVal)
|
|
|
|
{
|
|
|
|
if (mAppLocales.IsEmpty()) {
|
2017-03-14 22:28:47 +00:00
|
|
|
NegotiateAppLocales(mAppLocales);
|
2017-03-13 15:31:43 +00:00
|
|
|
}
|
|
|
|
aRetVal = mAppLocales[0];
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::GetAppLocaleAsBCP47(nsACString& aRetVal)
|
2017-02-05 20:29:39 +00:00
|
|
|
{
|
2017-02-08 21:12:16 +00:00
|
|
|
if (mAppLocales.IsEmpty()) {
|
2017-03-14 22:28:47 +00:00
|
|
|
NegotiateAppLocales(mAppLocales);
|
2017-02-08 21:12:16 +00:00
|
|
|
}
|
|
|
|
aRetVal = mAppLocales[0];
|
Bug 1348042 - Refactor LocaleService to operate in server-client mode. r=Ehsan,qdot
LocaleService serves two main functions. It is a central place for all code in the
engine to learn about locales, but it also does the language negotiation and selection.
The former is relevant in all processes, but the latter should only be performed
by the "main" process. In case of current Desktop Firefox, the parent process
is the one performing all the language negotiation, and content processes should
operate in the "client" mode.
In Fennec, there's a Java app on top of Gecko which should work as a "server"
and then all processes, including parent process of Gecko is merely a "client" for that.
This refactor finalizes this duality making it easily configurable to define in
which mode a given LocaleService operates.
The server-client model allows all clients to stay in sync with the server,
but operate transparently for all callers just returning the right values.
In order to initialize LocaleService in the client mode in child process with the
right locales I'm adding the list of app locales to the XPCOMInitData,
and then fire LocaleService::SetAppLocales in the child process initialization.
In order to keep the list up to date, I'm adding intl:app-locales-changed to
the list of observed topics, and when triggered, I send the updated list
to the child process, which updates LocaleService::SetAppLocales with the new
list.
MozReview-Commit-ID: K9X6berF3IO
--HG--
extra : rebase_source : ca5e502d064023fddfd63fe6fe5eccefce8dee52
2017-03-26 05:09:45 +00:00
|
|
|
|
2017-03-13 15:31:43 +00:00
|
|
|
SanitizeForBCP47(aRetVal);
|
2017-02-05 20:29:39 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2017-02-25 01:23:39 +00:00
|
|
|
|
2017-07-08 20:54:04 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::GetRegionalPrefsLocales(uint32_t* aCount, char*** aOutArray)
|
|
|
|
{
|
|
|
|
AutoTArray<nsCString,10> rgLocales;
|
|
|
|
|
|
|
|
GetRegionalPrefsLocales(rgLocales);
|
|
|
|
|
|
|
|
*aCount = rgLocales.Length();
|
|
|
|
*aOutArray = static_cast<char**>(moz_xmalloc(*aCount * sizeof(char*)));
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < *aCount; i++) {
|
|
|
|
(*aOutArray)[i] = moz_xstrdup(rgLocales[i].get());
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-02-25 01:23:39 +00:00
|
|
|
static LocaleService::LangNegStrategy
|
|
|
|
ToLangNegStrategy(int32_t aStrategy)
|
|
|
|
{
|
|
|
|
switch (aStrategy) {
|
|
|
|
case 1:
|
|
|
|
return LocaleService::LangNegStrategy::Matching;
|
|
|
|
case 2:
|
|
|
|
return LocaleService::LangNegStrategy::Lookup;
|
|
|
|
default:
|
|
|
|
return LocaleService::LangNegStrategy::Filtering;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::NegotiateLanguages(const char** aRequested,
|
|
|
|
const char** aAvailable,
|
|
|
|
const char* aDefaultLocale,
|
|
|
|
int32_t aStrategy,
|
|
|
|
uint32_t aRequestedCount,
|
|
|
|
uint32_t aAvailableCount,
|
|
|
|
uint32_t* aCount, char*** aRetVal)
|
|
|
|
{
|
|
|
|
if (aStrategy < 0 || aStrategy > 2) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the given string contains only ASCII characters valid in tags
|
|
|
|
// (i.e. alphanumerics, plus '-' and '_'), and is non-empty.
|
|
|
|
auto validTagChars = [](const char* s) {
|
2017-03-20 19:14:58 +00:00
|
|
|
if (!s || !*s) {
|
2017-02-25 01:23:39 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
while (*s) {
|
|
|
|
if (isalnum((unsigned char)*s) || *s == '-' || *s == '_' || *s == '*') {
|
|
|
|
s++;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
AutoTArray<nsCString, 100> requestedLocales;
|
|
|
|
for (uint32_t i = 0; i < aRequestedCount; i++) {
|
|
|
|
if (!validTagChars(aRequested[i])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
requestedLocales.AppendElement(aRequested[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
AutoTArray<nsCString, 100> availableLocales;
|
|
|
|
for (uint32_t i = 0; i < aAvailableCount; i++) {
|
|
|
|
if (!validTagChars(aAvailable[i])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
availableLocales.AppendElement(aAvailable[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoCString defaultLocale(aDefaultLocale);
|
|
|
|
|
|
|
|
LangNegStrategy strategy = ToLangNegStrategy(aStrategy);
|
|
|
|
|
|
|
|
AutoTArray<nsCString, 100> supportedLocales;
|
|
|
|
bool result = NegotiateLanguages(requestedLocales, availableLocales,
|
|
|
|
defaultLocale, strategy, supportedLocales);
|
|
|
|
|
|
|
|
if (!result) {
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aRetVal =
|
|
|
|
static_cast<char**>(moz_xmalloc(sizeof(char*) * supportedLocales.Length()));
|
|
|
|
|
|
|
|
*aCount = 0;
|
|
|
|
for (const auto& supported : supportedLocales) {
|
|
|
|
(*aRetVal)[(*aCount)++] = moz_xstrdup(supported.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
LocaleService::Locale::Locale(const nsCString& aLocale, bool aRange)
|
|
|
|
: mLocaleStr(aLocale)
|
|
|
|
{
|
|
|
|
int32_t partNum = 0;
|
|
|
|
|
|
|
|
nsAutoCString normLocale(aLocale);
|
|
|
|
normLocale.ReplaceChar('_', '-');
|
|
|
|
|
2017-06-20 09:19:52 +00:00
|
|
|
for (const nsACString& part : normLocale.Split('-')) {
|
2017-02-25 01:23:39 +00:00
|
|
|
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) {
|
|
|
|
mVariant.Assign(part);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
partNum++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aRange) {
|
|
|
|
if (mLanguage.IsEmpty()) {
|
2017-09-06 08:35:56 +00:00
|
|
|
mLanguage.AssignLiteral("*");
|
2017-02-25 01:23:39 +00:00
|
|
|
}
|
|
|
|
if (mScript.IsEmpty()) {
|
2017-09-06 08:35:56 +00:00
|
|
|
mScript.AssignLiteral("*");
|
2017-02-25 01:23:39 +00:00
|
|
|
}
|
|
|
|
if (mRegion.IsEmpty()) {
|
2017-09-06 08:35:56 +00:00
|
|
|
mRegion.AssignLiteral("*");
|
2017-02-25 01:23:39 +00:00
|
|
|
}
|
|
|
|
if (mVariant.IsEmpty()) {
|
2017-09-06 08:35:56 +00:00
|
|
|
mVariant.AssignLiteral("*");
|
2017-02-25 01:23:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-03 00:39:17 +00:00
|
|
|
static bool
|
|
|
|
SubtagMatches(const nsCString& aSubtag1, const nsCString& aSubtag2)
|
|
|
|
{
|
|
|
|
return aSubtag1.EqualsLiteral("*") ||
|
|
|
|
aSubtag2.EqualsLiteral("*") ||
|
|
|
|
aSubtag1.Equals(aSubtag2, nsCaseInsensitiveCStringComparator());
|
|
|
|
}
|
|
|
|
|
2017-02-25 01:23:39 +00:00
|
|
|
bool
|
|
|
|
LocaleService::Locale::Matches(const LocaleService::Locale& aLocale) const
|
|
|
|
{
|
2017-03-03 00:39:17 +00:00
|
|
|
return SubtagMatches(mLanguage, aLocale.mLanguage) &&
|
|
|
|
SubtagMatches(mScript, aLocale.mScript) &&
|
|
|
|
SubtagMatches(mRegion, aLocale.mRegion) &&
|
|
|
|
SubtagMatches(mVariant, aLocale.mVariant);
|
|
|
|
}
|
2017-02-25 01:23:39 +00:00
|
|
|
|
2017-03-03 00:39:17 +00:00
|
|
|
bool
|
|
|
|
LocaleService::Locale::LanguageMatches(const LocaleService::Locale& aLocale) const
|
|
|
|
{
|
|
|
|
return SubtagMatches(mLanguage, aLocale.mLanguage) &&
|
|
|
|
SubtagMatches(mScript, aLocale.mScript);
|
2017-02-25 01:23:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
LocaleService::Locale::SetVariantRange()
|
|
|
|
{
|
|
|
|
mVariant.AssignLiteral("*");
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
LocaleService::Locale::SetRegionRange()
|
|
|
|
{
|
|
|
|
mRegion.AssignLiteral("*");
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
LocaleService::Locale::AddLikelySubtags()
|
Bug 1400006 - Extend language negotiation in LocaleService to support looking for the best likelySubtag for the locale with region stripped. r=Pike
Add additional logic to our language negotation to do apply likelySubtags when a direct match is not available.
Currently, if the user specifies the locale with region, and we do not have a direct for that region, we pick all locales for the same language and other regions in no order.
The example of where it returns suboptimal results:
1) Requested locale "en-CA"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-ZA", "en-GB", "en-US"]
This would not happen, if the user requested a generic "de", "en" etc.:
1) Requested locale "en"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-US", "en-ZA", "en-GB"]
because after not finding a direct match, we would use likelySubtags to extend "en" to "en-Latn-US" and then find the priority match in "en-US".
This patch extends this logic to "en-US" or "de-LU" by adding a step which strips the region tag and then applies likelySubtag on the result.
This means that in absence of direct match the following fallbacks would happen:
"de-LU" -> "de-DE"
"es-CL" -> "es-ES"
"en-CA" -> "en-US"
This does not affect languages that use multiple scripts, so ar, sr and zh are not affected.
MozReview-Commit-ID: BR1WrgXSf6a
--HG--
extra : rebase_source : abc205c4f993680ab0cd0c8b8c016543d5462d01
2017-09-14 22:21:33 +00:00
|
|
|
{
|
|
|
|
return AddLikelySubtagsForLocale(mLocaleStr);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
LocaleService::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
|
|
|
|
LocaleService::Locale::AddLikelySubtagsForLocale(const nsACString& aLocale)
|
2017-02-25 01:23:39 +00:00
|
|
|
{
|
|
|
|
const int32_t kLocaleMax = 160;
|
|
|
|
char maxLocale[kLocaleMax];
|
Bug 1400006 - Extend language negotiation in LocaleService to support looking for the best likelySubtag for the locale with region stripped. r=Pike
Add additional logic to our language negotation to do apply likelySubtags when a direct match is not available.
Currently, if the user specifies the locale with region, and we do not have a direct for that region, we pick all locales for the same language and other regions in no order.
The example of where it returns suboptimal results:
1) Requested locale "en-CA"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-ZA", "en-GB", "en-US"]
This would not happen, if the user requested a generic "de", "en" etc.:
1) Requested locale "en"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-US", "en-ZA", "en-GB"]
because after not finding a direct match, we would use likelySubtags to extend "en" to "en-Latn-US" and then find the priority match in "en-US".
This patch extends this logic to "en-US" or "de-LU" by adding a step which strips the region tag and then applies likelySubtag on the result.
This means that in absence of direct match the following fallbacks would happen:
"de-LU" -> "de-DE"
"es-CL" -> "es-ES"
"en-CA" -> "en-US"
This does not affect languages that use multiple scripts, so ar, sr and zh are not affected.
MozReview-Commit-ID: BR1WrgXSf6a
--HG--
extra : rebase_source : abc205c4f993680ab0cd0c8b8c016543d5462d01
2017-09-14 22:21:33 +00:00
|
|
|
nsAutoCString locale(aLocale);
|
2017-02-25 01:23:39 +00:00
|
|
|
|
|
|
|
UErrorCode status = U_ZERO_ERROR;
|
Bug 1400006 - Extend language negotiation in LocaleService to support looking for the best likelySubtag for the locale with region stripped. r=Pike
Add additional logic to our language negotation to do apply likelySubtags when a direct match is not available.
Currently, if the user specifies the locale with region, and we do not have a direct for that region, we pick all locales for the same language and other regions in no order.
The example of where it returns suboptimal results:
1) Requested locale "en-CA"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-ZA", "en-GB", "en-US"]
This would not happen, if the user requested a generic "de", "en" etc.:
1) Requested locale "en"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-US", "en-ZA", "en-GB"]
because after not finding a direct match, we would use likelySubtags to extend "en" to "en-Latn-US" and then find the priority match in "en-US".
This patch extends this logic to "en-US" or "de-LU" by adding a step which strips the region tag and then applies likelySubtag on the result.
This means that in absence of direct match the following fallbacks would happen:
"de-LU" -> "de-DE"
"es-CL" -> "es-ES"
"en-CA" -> "en-US"
This does not affect languages that use multiple scripts, so ar, sr and zh are not affected.
MozReview-Commit-ID: BR1WrgXSf6a
--HG--
extra : rebase_source : abc205c4f993680ab0cd0c8b8c016543d5462d01
2017-09-14 22:21:33 +00:00
|
|
|
uloc_addLikelySubtags(locale.get(), maxLocale, kLocaleMax, &status);
|
2017-02-25 01:23:39 +00:00
|
|
|
|
|
|
|
if (U_FAILURE(status)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsDependentCString maxLocStr(maxLocale);
|
|
|
|
Locale loc = Locale(maxLocStr, false);
|
|
|
|
|
|
|
|
if (loc == *this) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mLanguage = loc.mLanguage;
|
|
|
|
mScript = loc.mScript;
|
|
|
|
mRegion = loc.mRegion;
|
Bug 1400006 - Extend language negotiation in LocaleService to support looking for the best likelySubtag for the locale with region stripped. r=Pike
Add additional logic to our language negotation to do apply likelySubtags when a direct match is not available.
Currently, if the user specifies the locale with region, and we do not have a direct for that region, we pick all locales for the same language and other regions in no order.
The example of where it returns suboptimal results:
1) Requested locale "en-CA"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-ZA", "en-GB", "en-US"]
This would not happen, if the user requested a generic "de", "en" etc.:
1) Requested locale "en"
2) Available locales ["en-ZA", "en-GB", "en-US"]
3) Negotiated locales ["en-US", "en-ZA", "en-GB"]
because after not finding a direct match, we would use likelySubtags to extend "en" to "en-Latn-US" and then find the priority match in "en-US".
This patch extends this logic to "en-US" or "de-LU" by adding a step which strips the region tag and then applies likelySubtag on the result.
This means that in absence of direct match the following fallbacks would happen:
"de-LU" -> "de-DE"
"es-CL" -> "es-ES"
"en-CA" -> "en-US"
This does not affect languages that use multiple scripts, so ar, sr and zh are not affected.
MozReview-Commit-ID: BR1WrgXSf6a
--HG--
extra : rebase_source : abc205c4f993680ab0cd0c8b8c016543d5462d01
2017-09-14 22:21:33 +00:00
|
|
|
|
|
|
|
// We don't update variant from likelySubtag since it's not going to
|
|
|
|
// provide it and we want to preserve the range
|
|
|
|
|
2017-02-25 01:23:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
2017-03-06 21:24:45 +00:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::GetRequestedLocales(uint32_t* aCount, char*** aOutArray)
|
|
|
|
{
|
|
|
|
AutoTArray<nsCString, 16> requestedLocales;
|
|
|
|
bool res = GetRequestedLocales(requestedLocales);
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
NS_ERROR("Couldn't retrieve selected locales from prefs!");
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aCount = requestedLocales.Length();
|
|
|
|
*aOutArray = CreateOutArray(requestedLocales);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2017-03-12 19:34:56 +00:00
|
|
|
|
2017-03-12 02:43:11 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::GetRequestedLocale(nsACString& aRetVal)
|
|
|
|
{
|
|
|
|
AutoTArray<nsCString, 16> requestedLocales;
|
|
|
|
bool res = GetRequestedLocales(requestedLocales);
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
NS_ERROR("Couldn't retrieve selected locales from prefs!");
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (requestedLocales.Length() > 0) {
|
|
|
|
aRetVal = requestedLocales[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2017-03-12 19:34:56 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::SetRequestedLocales(const char** aRequested,
|
|
|
|
uint32_t aRequestedCount)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aRequestedCount < 2, "We can only handle one requested locale");
|
|
|
|
|
|
|
|
if (aRequestedCount == 0) {
|
|
|
|
Preferences::ClearUser(SELECTED_LOCALE_PREF);
|
|
|
|
} else {
|
|
|
|
Preferences::SetCString(SELECTED_LOCALE_PREF, aRequested[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Preferences::SetBool(MATCH_OS_LOCALE_PREF, aRequestedCount == 0);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2017-03-14 00:33:24 +00:00
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::GetAvailableLocales(uint32_t* aCount, char*** aOutArray)
|
|
|
|
{
|
|
|
|
AutoTArray<nsCString, 100> availableLocales;
|
|
|
|
bool res = GetAvailableLocales(availableLocales);
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
NS_ERROR("Couldn't retrieve available locales!");
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aCount = availableLocales.Length();
|
|
|
|
*aOutArray = CreateOutArray(availableLocales);
|
2017-03-14 19:35:06 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2017-03-14 00:33:24 +00:00
|
|
|
|
2017-03-14 19:35:06 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
LocaleService::GetIsAppLocaleRTL(bool* aRetVal)
|
|
|
|
{
|
|
|
|
(*aRetVal) = IsAppLocaleRTL();
|
2017-03-14 00:33:24 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|