Bug 1213450 - Complete the implementation of chrome.i18n - getAcceptLanguages, r?kmag draft
authorbsilverberg <bsilverberg@mozilla.com>
Thu, 25 Feb 2016 07:29:09 -0500
changeset 334559 2339e42a83460682a8d6df206c10849b38a288eb
parent 334557 1c779b0b8969649dee0a8d48552ca314b36b1d0a
child 514929 c2fc7e15cde44b712321f0d5f6ef65be07fd5ea9
push id11568
push userbmo:bob.silverberg@gmail.com
push dateThu, 25 Feb 2016 13:04:31 +0000
reviewerskmag
bugs1213450
milestone47.0a1
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"),
     };
   }