Bug 1213450 - Complete the implementation of chrome.i18n - getAcceptLanguages, r=kmag
authorbsilverberg <bsilverberg@mozilla.com>
Thu, 25 Feb 2016 07:29:09 -0500
changeset 321929 b34dbc48da45cb1c06e9088835513837bf0ff6ca
parent 321928 1c779b0b8969649dee0a8d48552ca314b36b1d0a
child 321930 5c0b8b63d2a8ed080b6f785b641dbd059ecd2bf4
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1213450
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1213450 - Complete the implementation of chrome.i18n - getAcceptLanguages, r=kmag Implement browser.i18n.getAcceptLanguages() including tests Rebase against fx-team, resolving some conflicts Fix eslint errors/warnings MozReview-Commit-ID: 52sZWsIHbl4
toolkit/components/extensions/ExtensionContent.jsm
toolkit/components/extensions/ExtensionUtils.jsm
toolkit/components/extensions/ext-i18n.js
toolkit/components/extensions/schemas/i18n.json
toolkit/components/extensions/test/mochitest/test_ext_i18n.html
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -115,16 +115,21 @@ var api = context => {
       inIncognitoContext: PrivateBrowsingUtils.isContentWindowPrivate(context.contentWindow),
     },
 
     i18n: {
       getMessage: function(messageName, substitutions) {
         return context.extension.localizeMessage(messageName, substitutions);
       },
 
+      getAcceptLanguages: function(callback) {
+        let result = context.extension.localeData.acceptLanguages;
+        return context.wrapPromise(Promise.resolve(result), callback);
+      },
+
       getUILanguage: function() {
         return context.extension.localeData.uiLocale;
       },
 
       detectLanguage: function(text, callback) {
         let result = detectLanguage(text);
         return context.wrapPromise(result, callback);
       },
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -15,16 +15,18 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
                                   "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector",
                                   "resource:///modules/translation/LanguageDetector.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Locale",
                                   "resource://gre/modules/Locale.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+                                  "resource://gre/modules/Preferences.jsm");
 
 function filterStack(error) {
   return String(error.stack).replace(/(^.*(Task\.jsm|Promise-backend\.js).*\n)+/gm, "<Promise Chain>\n");
 }
 
 // Run a function and report exceptions.
 function runSafeSyncWithoutClone(f, ...args) {
   try {
@@ -424,16 +426,26 @@ LocaleData.prototype = {
       // Message names are also case-insensitive, so normalize them to lower-case.
       result.set(key.toLowerCase(), value);
     }
 
     this.messages.set(locale, result);
     return result;
   },
 
+  get acceptLanguages() {
+    let result = Preferences.get("intl.accept_languages", "", Ci.nsIPrefLocalizedString);
+    result = result.split(",");
+    result = result.map(lang => {
+      return lang.replace(/-/g, "_").trim();
+    });
+    return result;
+  },
+
+
   get uiLocale() {
     // Return the browser locale, but convert it to a Chrome-style
     // locale code.
     return Locale.getLocale().replace(/-/g, "_");
   },
 };
 
 // This is a generic class for managing event listeners. Example usage:
--- a/toolkit/components/extensions/ext-i18n.js
+++ b/toolkit/components/extensions/ext-i18n.js
@@ -7,16 +7,21 @@ var {
 
 extensions.registerSchemaAPI("i18n", null, (extension, context) => {
   return {
     i18n: {
       getMessage: function(messageName, substitutions) {
         return extension.localizeMessage(messageName, substitutions);
       },
 
+      getAcceptLanguages: function() {
+        let result = extension.localeData.acceptLanguages;
+        return Promise.resolve(result);
+      },
+
       getUILanguage: function() {
         return extension.localeData.uiLocale;
       },
 
       detectLanguage: function(text) {
         return detectLanguage(text);
       },
     },
--- a/toolkit/components/extensions/schemas/i18n.json
+++ b/toolkit/components/extensions/schemas/i18n.json
@@ -25,17 +25,16 @@
         "id": "LanguageCode",
         "type": "string",
         "description": "An ISO language code such as <code>en</code> or <code>fr</code>. For a complete list of languages supported by this method, see <a href='http://src.chromium.org/viewvc/chrome/trunk/src/third_party/cld/languages/internal/languages.cc'>kLanguageInfoTable</a>. For an unknown language, <code>und</code> will be returned, which means that [percentage] of the text is unknown to CLD"
       }
     ],
     "functions": [
       {
         "name": "getAcceptLanguages",
-        "unsupported": true,
         "type": "function",
         "description": "Gets the accept-languages of the browser. This is different from the locale used by the browser; to get the locale, use $(ref:i18n.getUILanguage).",
         "async": "callback",
         "parameters": [
           {
             "type": "function",
             "name": "callback",
             "parameters": [
--- a/toolkit/components/extensions/test/mochitest/test_ext_i18n.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_i18n.html
@@ -9,16 +9,17 @@
   <script type="text/javascript" src="head.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 
 <script type="text/javascript">
 "use strict";
 
+SimpleTest.registerCleanupFunction(() => { SpecialPowers.clearUserPref("intl.accept_languages"); });
 SimpleTest.registerCleanupFunction(() => { SpecialPowers.clearUserPref("general.useragent.locale"); });
 
 add_task(function* test_i18n() {
   function runTests(assertEq) {
     let _ = browser.i18n.getMessage.bind(browser.i18n);
 
     let url = browser.runtime.getURL("/");
     assertEq(url, `moz-extension://${_("@@extension_id")}/`, "@@extension_id builtin message");
@@ -158,16 +159,95 @@ add_task(function* test_i18n() {
   let win = window.open("file_sample.html");
   yield extension.awaitMessage("content-script-finished");
   win.close();
 
   yield extension.awaitFinish("l10n");
   yield extension.unload();
 });
 
+add_task(function* test_get_accept_languages() {
+  function background() {
+    function checkResults(source, results, expected) {
+      browser.test.assertEq(
+        expected.length,
+        results.length,
+        `got expected number of languages in ${source}`);
+      results.forEach((lang, index) => {
+        browser.test.assertEq(
+          expected[index],
+          lang,
+          `got expected language in ${source}`);
+      });
+    }
+
+    let tabId;
+
+    browser.tabs.query({currentWindow: true, active: true}, tabs => {
+      tabId = tabs[0].id;
+      browser.test.sendMessage("ready");
+    });
+
+    browser.test.onMessage.addListener(([msg, expected]) => {
+      Promise.all([
+        new Promise(
+          resolve => browser.tabs.sendMessage(tabId, "get-results", resolve)),
+        browser.i18n.getAcceptLanguages(),
+      ]).then(([contentResults, backgroundResults]) => {
+        checkResults("contentScript", contentResults, expected);
+        checkResults("background", backgroundResults, expected);
+
+        browser.test.sendMessage("done");
+      });
+    });
+  }
+
+  function content() {
+    browser.runtime.onMessage.addListener((msg, sender, respond) => {
+      browser.i18n.getAcceptLanguages(respond);
+      return true;
+    });
+  }
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "content_scripts": [{
+        "matches": ["http://mochi.test/*/file_sample.html"],
+        "run_at": "document_start",
+        "js": ["content_script.js"],
+      }],
+    },
+
+    background: `(${background})()`,
+
+    files: {
+      "content_script.js": `(${content})()`,
+    },
+  });
+
+  let win = window.open("file_sample.html");
+
+  yield extension.startup();
+  yield extension.awaitMessage("ready");
+
+  let expectedLangs = ["en_US", "en"];
+  extension.sendMessage(["expect-results", expectedLangs]);
+  yield extension.awaitMessage("done");
+
+  expectedLangs = ["en_US", "en", "fr_CA", "fr"];
+  SpecialPowers.setCharPref("intl.accept_languages", expectedLangs.toString());
+  extension.sendMessage(["expect-results", expectedLangs]);
+  yield extension.awaitMessage("done");
+  SpecialPowers.clearUserPref("intl.accept_languages");
+
+  win.close();
+
+  yield extension.unload();
+});
+
 add_task(function* test_get_ui_language() {
   function getResults() {
     return {
       getUILanguage: browser.i18n.getUILanguage(),
       getMessage: browser.i18n.getMessage("@@ui_locale"),
     };
   }