Bug 1346617 - Add LocaleService::SetRequestedLocales. r=jfkthame draft
authorZibi Braniecki <gandalf@mozilla.com>
Sun, 12 Mar 2017 12:34:56 -0700
changeset 498108 a2f1b5c31317c80666fbda334ea7bfcef28397b6
parent 497714 d706713c339ac02c2114a98f7c26ecd449d8134b
child 549091 7cb98af21cea7b29f1c2b08354b5e8d7e0a045bc
push id49118
push userzbraniecki@mozilla.com
push dateTue, 14 Mar 2017 10:41:06 +0000
reviewersjfkthame
bugs1346617
milestone55.0a1
Bug 1346617 - Add LocaleService::SetRequestedLocales. r=jfkthame MozReview-Commit-ID: FrziO9fOs3R
intl/locale/LocaleService.cpp
intl/locale/LocaleService.h
intl/locale/mozILocaleService.idl
intl/locale/tests/unit/test_localeService.js
mobile/android/chrome/content/browser.js
--- a/intl/locale/LocaleService.cpp
+++ b/intl/locale/LocaleService.cpp
@@ -138,18 +138,16 @@ LocaleService::GetAppLocalesAsBCP47(nsTA
   }
 }
 
 bool
 LocaleService::GetRequestedLocales(nsTArray<nsCString>& aRetVal)
 {
   nsAutoCString locale;
 
-  nsCOMPtr<nsIPrefBranch> prefs(do_GetService("@mozilla.org/preferences-service;1"));
-
   // 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;
@@ -158,17 +156,16 @@ LocaleService::GetRequestedLocales(nsTAr
 
   // Otherwise, we'll try to get the requested locale from the prefs.
 
   // In some cases, mainly on Fennec and on Linux version,
   // `general.useragent.locale` is a special 'localized' value, like:
   // "chrome://global/locale/intl.properties"
   if (!NS_SUCCEEDED(Preferences::GetLocalizedCString(SELECTED_LOCALE_PREF, &locale))) {
     // If not, we can attempt to retrieve it as a simple string value.
-    //rv = prefs->GetCharPref(SELECTED_LOCALE_PREF, getter_Copies(locale));
     if (!NS_SUCCEEDED(Preferences::GetCString(SELECTED_LOCALE_PREF, &locale))) {
       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);
@@ -636,8 +633,24 @@ LocaleService::GetRequestedLocales(uint3
     return NS_ERROR_FAILURE;
   }
 
   *aCount = requestedLocales.Length();
   *aOutArray = CreateOutArray(requestedLocales);
 
   return NS_OK;
 }
+
+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;
+}
--- a/intl/locale/LocaleService.h
+++ b/intl/locale/LocaleService.h
@@ -1,17 +1,16 @@
 /* -*- 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/. */
 
 #ifndef mozilla_intl_LocaleService_h__
 #define mozilla_intl_LocaleService_h__
 
-#include "mozilla/StaticPtr.h"
 #include "nsIObserver.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 #include "mozILocaleService.h"
 
 namespace mozilla {
 namespace intl {
--- a/intl/locale/mozILocaleService.idl
+++ b/intl/locale/mozILocaleService.idl
@@ -128,9 +128,28 @@ interface mozILocaleService : nsISupport
    *
    * The result is an ordered list of locale IDs which should be
    * used as a requestedLocales input list for language negotiation.
    *
    * Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
    */
   void getRequestedLocales([optional] out unsigned long aCount,
                            [retval, array, size_is(aCount)] out string aLocales);
+
+  /**
+   * Sets a list of locales that the user requested the app to be
+   * localized to.
+   *
+   * The argument is an ordered list of locale IDs which should be
+   * used as a requestedLocales input list for language negotiation.
+   *
+   * The current implementation is limited to handle at most one
+   * locale passed to the API. In the future we'll transition to support
+   * whole fallback chain.
+   *
+   * If an empty list is passed, the list of requested locales will
+   * be picked from the operating system.
+   *
+   * Example: ["de"]
+   */
+  void setRequestedLocales([array, size_is(aRequestedCount)] in string aRequested,
+                           [optional] in unsigned long aRequestedCount);
 };
--- a/intl/locale/tests/unit/test_localeService.js
+++ b/intl/locale/tests/unit/test_localeService.js
@@ -5,33 +5,29 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const { Services } = Cu.import('resource://gre/modules/Services.jsm', {});
 const osPrefs = Cc["@mozilla.org/intl/ospreferences;1"].
   getService(Ci.mozIOSPreferences);
 
+const localeService =
+  Components.classes["@mozilla.org/intl/localeservice;1"]
+  .getService(Components.interfaces.mozILocaleService);
+
 /**
  * Make sure the locale service can be instantiated.
  */
 function run_test()
 {
-  const localeService =
-    Components.classes["@mozilla.org/intl/localeservice;1"]
-    .getService(Components.interfaces.mozILocaleService);
-
   run_next_test();
 }
 
 add_test(function test_getAppLocalesAsLangTags() {
-  const localeService =
-    Components.classes["@mozilla.org/intl/localeservice;1"]
-    .getService(Components.interfaces.mozILocaleService);
-
   const appLocale = localeService.getAppLocaleAsLangTag();
   do_check_true(appLocale != "", "appLocale is non-empty");
 
   const appLocales = localeService.getAppLocalesAsLangTags();
   do_check_true(Array.isArray(appLocales), "appLocales returns an array");
 
   do_check_true(appLocale == appLocales[0], "appLocale matches first entry in appLocales");
 
@@ -41,20 +37,16 @@ add_test(function test_getAppLocalesAsLa
   run_next_test();
 });
 
 const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
 const PREF_SELECTED_LOCALE = "general.useragent.locale";
 const REQ_LOC_CHANGE_EVENT = "intl:requested-locales-changed";
 
 add_test(function test_getRequestedLocales() {
-  const localeService =
-    Components.classes["@mozilla.org/intl/localeservice;1"]
-    .getService(Components.interfaces.mozILocaleService);
-
   const requestedLocales = localeService.getRequestedLocales();
   do_check_true(Array.isArray(requestedLocales), "requestedLocales returns an array");
 
   run_next_test();
 });
 
 /**
  * In this test we verify that after we set an observer on the LocaleService
@@ -62,20 +54,16 @@ add_test(function test_getRequestedLocal
  * pref for matchOS is set to true.
  *
  * Then, we test that when the matchOS is set to true, we will retrieve
  * OS locale from getRequestedLocales.
  */
 add_test(function test_getRequestedLocales_matchOS() {
   do_test_pending();
 
-  const localeService =
-    Components.classes["@mozilla.org/intl/localeservice;1"]
-    .getService(Components.interfaces.mozILocaleService);
-
   Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
   Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "ar-IR");
 
   const observer = {
     observe: function (aSubject, aTopic, aData) {
       switch (aTopic) {
         case REQ_LOC_CHANGE_EVENT:
           const reqLocs = localeService.getRequestedLocales();
@@ -95,20 +83,16 @@ add_test(function test_getRequestedLocal
 /**
  * In this test we verify that after we set an observer on the LocaleService
  * event for requested locales change, it will be fired when the
  * pref for browser UI locale changes.
  */
 add_test(function test_getRequestedLocales_matchOS() {
   do_test_pending();
 
-  const localeService =
-    Components.classes["@mozilla.org/intl/localeservice;1"]
-    .getService(Components.interfaces.mozILocaleService);
-
   Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
   Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "ar-IR");
 
   const observer = {
     observe: function (aSubject, aTopic, aData) {
       switch (aTopic) {
         case REQ_LOC_CHANGE_EVENT:
           const reqLocs = localeService.getRequestedLocales();
@@ -120,12 +104,28 @@ add_test(function test_getRequestedLocal
   };
 
   Services.obs.addObserver(observer, REQ_LOC_CHANGE_EVENT, false);
   Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "sr-RU");
 
   run_next_test();
 });
 
+add_test(function test_setRequestedLocales() {
+  localeService.setRequestedLocales([]);
+
+  let matchOS = Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE);
+  do_check_true(matchOS === true);
+
+  localeService.setRequestedLocales(['de-AT']);
+
+  matchOS = Services.prefs.getBoolPref(PREF_MATCH_OS_LOCALE);
+  do_check_true(matchOS === false);
+  let locales = localeService.getRequestedLocales();;
+  do_check_true(locales[0] === 'de-AT');
+
+  run_next_test();
+});
+
 do_register_cleanup(() => {
     Services.prefs.clearUserPref(PREF_SELECTED_LOCALE);
     Services.prefs.clearUserPref(PREF_MATCH_OS_LOCALE);
 });
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1711,39 +1711,21 @@ var BrowserApp = {
         let appLocale = this.getUALocalePref();
 
         this.computeAcceptLanguages(languageTag, appLocale);
         break;
       }
 
       case "Locale:Changed": {
         if (data) {
-          // The value provided to Locale:Changed should be a BCP47 language tag
-          // understood by Gecko -- for example, "es-ES" or "de".
-          console.log("Locale:Changed: " + data.languageTag);
-
-          // We always write a localized pref, even though sometimes the value is a char pref.
-          // (E.g., on desktop single-locale builds.)
-          this.setLocalizedPref("general.useragent.locale", data.languageTag);
+          Services.locale.setRequestedLocales([data.languageTag]);
         } else {
-          // Resetting.
-          console.log("Switching to system locale.");
-          Services.prefs.clearUserPref("general.useragent.locale");
+          Services.locale.setRequestedLocales([]);
         }
 
-        Services.prefs.setBoolPref("intl.locale.matchOS", !data);
-
-        // Ensure that this choice is immediately persisted, because
-        // Gecko won't be told again if it forgets.
-        Services.prefs.savePrefFile(null);
-
-        // Blow away the string cache so that future lookups get the
-        // correct locale.
-        Strings.flush();
-
         // Make sure we use the right Accept-Language header.
         let osLocale;
         try {
           // This should never not be set at this point, but better safe than sorry.
           osLocale = Services.prefs.getCharPref("intl.locale.os");
         } catch (e) {
         }