Bug 1475903 - Add mozIDOMLocalization API. r?mossop draft
authorZibi Braniecki <zbraniecki@mozilla.com>
Fri, 22 Jun 2018 13:14:23 -0700
changeset 825712 b4fbb1a9c401649614f58078ebba6404c19ab73d
parent 825336 af6a7edf0069549543f2fba6a8ee3ea251b20829
child 826336 1dd0a9fba23044279ba5d80e57c85b7312d20794
push id118151
push userbmo:gandalf@aviary.pl
push dateThu, 02 Aug 2018 05:07:29 +0000
reviewersmossop
bugs1475903
milestone63.0a1
Bug 1475903 - Add mozIDOMLocalization API. r?mossop In order to use DOMLocalization from C++ we need an XPIDL interface. mozIDOMLocalization exposes the class and functionality allowing DocumentL10n to hook into it. MozReview-Commit-ID: GPMhw61LPEg
browser/installer/package-manifest.in
intl/l10n/moz.build
intl/l10n/mozDOMLocalization.js
intl/l10n/mozDOMLocalization.manifest
intl/l10n/mozIDOMLocalization.idl
intl/l10n/test/chrome.ini
intl/l10n/test/dom/test_mozdom_translateElements.html
intl/l10n/test/dom/test_mozdom_translateFragment.html
intl/l10n/test/dom/test_mozdom_translateRoots.html
intl/l10n/test/test_mozdomlocalization.js
intl/l10n/test/xpcshell.ini
mobile/android/installer/package-manifest.in
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -359,16 +359,19 @@
 @RESPATH@/components/TestInterfaceJSMaplike.js
 #endif
 
 #if defined(MOZ_DEBUG) || defined(MOZ_DEV_EDITION) || defined(NIGHTLY_BUILD)
 @RESPATH@/browser/components/testComponents.manifest
 @RESPATH@/browser/components/startupRecorder.js
 #endif
 
+@RESPATH@/components/mozDOMLocalization.js
+@RESPATH@/components/mozDOMLocalization.manifest
+
 ; [Extensions]
 @RESPATH@/components/extensions-toolkit.manifest
 @RESPATH@/components/extension-process-script.js
 @RESPATH@/browser/components/extensions-browser.manifest
 
 ; Modules
 @RESPATH@/browser/modules/*
 @RESPATH@/modules/*
--- a/intl/l10n/moz.build
+++ b/intl/l10n/moz.build
@@ -6,16 +6,27 @@
 
 EXTRA_JS_MODULES += [
     'DOMLocalization.jsm',
     'L10nRegistry.jsm',
     'Localization.jsm',
     'MessageContext.jsm',
 ]
 
+XPIDL_SOURCES += [
+    'mozIDOMLocalization.idl',
+]
+
+XPIDL_MODULE = 'locale'
+
+EXTRA_COMPONENTS += [
+    'mozDOMLocalization.js',
+    'mozDOMLocalization.manifest',
+]
+
 XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini']
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
 JAR_MANIFESTS += ['jar.mn']
 
 SPHINX_TREES['l10n'] = 'docs'
 
new file mode 100644
--- /dev/null
+++ b/intl/l10n/mozDOMLocalization.js
@@ -0,0 +1,9 @@
+const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
+const { DOMLocalization } = ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm", {});
+
+DOMLocalization.prototype.classID =
+  Components.ID("{29cc3895-8835-4c5b-b53a-0c0d1a458dee}");
+DOMLocalization.prototype.QueryInterface =
+  ChromeUtils.generateQI([Ci.mozIDOMLocalization, Ci.nsISupportsWeakReference]);
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DOMLocalization]);
new file mode 100644
--- /dev/null
+++ b/intl/l10n/mozDOMLocalization.manifest
@@ -0,0 +1,2 @@
+component {29cc3895-8835-4c5b-b53a-0c0d1a458dee} mozDOMLocalization.js
+contract @mozilla.org/intl/domlocalization;1 {29cc3895-8835-4c5b-b53a-0c0d1a458dee}
new file mode 100644
--- /dev/null
+++ b/intl/l10n/mozIDOMLocalization.idl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+webidl Document;
+webidl Element;
+
+[scriptable, uuid(7c468500-541f-4fe0-98c9-92a53b63ec8d)]
+interface mozIDOMLocalization : nsISupports
+{
+  unsigned long addResourceIds(in Array<AString> resourceIds);
+  unsigned long removeResourceIds(in Array<AString> resourceIds);
+
+  Promise formatMessages(in Array<jsval> aKeys);
+  Promise formatValues(in Array<jsval> aKeys);
+  Promise formatValue(in DOMString aId, [optional] in jsval aArgs);
+
+  Promise translateFragment(in Element aElement);
+  Promise translateElements(in Array<Element> aElements);
+
+  void connectRoot(in Element aElement);
+  Promise translateRoots();
+  readonly attribute Promise ready;
+};
--- a/intl/l10n/test/chrome.ini
+++ b/intl/l10n/test/chrome.ini
@@ -9,8 +9,12 @@
 [dom/test_domloc_translateRoots.html]
 [dom/test_domloc_mutations.html]
 [dom/test_domloc_overlay.html]
 [dom/test_domloc_overlay_repeated.html]
 [dom/test_domloc_overlay_missing_all.html]
 [dom/test_domloc_overlay_missing_children.html]
 [dom/test_domloc_overlay_sanitized.html]
 [dom/test_domloc.xul]
+
+[dom/test_mozdom_translateElements.html]
+[dom/test_mozdom_translateFragment.html]
+[dom/test_mozdom_translateRoots.html]
new file mode 100644
--- /dev/null
+++ b/intl/l10n/test/dom/test_mozdom_translateElements.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test mozIDOMLocalization.translateElements</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <script type="application/javascript">
+  "use strict";
+  const domLocalization =
+    Cc["@mozilla.org/intl/domlocalization;1"].createInstance(
+      Ci.mozIDOMLocalization);
+  const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
+  const { L10nRegistry, FileSource } =
+    ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm", {});
+
+  const fs = {
+    "/localization/en-US/browser/menu.ftl": `
+title = Hello World
+link =
+  .title = Click me
+`,
+  };
+  const originalLoad = L10nRegistry.load;
+  const originalRequested = Services.locale.getRequestedLocales();
+
+  L10nRegistry.load = async function(url) {
+    return fs.hasOwnProperty(url) ? fs[url] : false;
+  };
+
+  const source = new FileSource("test", ["en-US"], "/localization/{locale}");
+  L10nRegistry.registerSource(source);
+
+  window.onload = async function() {
+    SimpleTest.waitForExplicitFinish();
+
+    domLocalization.addResourceIds(["/browser/menu.ftl"], 1);
+
+    const p1 = document.querySelectorAll("p")[0];
+    const link1 = document.querySelectorAll("a")[0];
+
+    await domLocalization.translateElements([p1, link1], 2);
+
+    is(p1.textContent, "Hello World");
+    is(link1.getAttribute("title"), "Click me");
+
+    // Cleanup
+    L10nRegistry.removeSource(source.name);
+    L10nRegistry.load = originalLoad;
+    Services.locale.setRequestedLocales(originalRequested);
+
+    SimpleTest.finish();
+  };
+  </script>
+</head>
+<body>
+  <p data-l10n-id="title" />
+  <a data-l10n-id="link" />
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/intl/l10n/test/dom/test_mozdom_translateFragment.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test mozIDOMLocalization.translateFragment</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <script type="application/javascript">
+  "use strict";
+  const domLocalization =
+    Cc["@mozilla.org/intl/domlocalization;1"].createInstance(
+      Ci.mozIDOMLocalization);
+  const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
+  const { L10nRegistry, FileSource } =
+    ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm", {});
+
+  const fs = {
+    "/localization/en-US/browser/menu.ftl": `
+title = Hello World
+subtitle = Welcome to Fluent
+`,
+  };
+  const originalLoad = L10nRegistry.load;
+  const originalRequested = Services.locale.getRequestedLocales();
+
+  L10nRegistry.load = async function(url) {
+    return fs.hasOwnProperty(url) ? fs[url] : false;
+  };
+
+  const source = new FileSource("test", ["en-US"], "/localization/{locale}");
+  L10nRegistry.registerSource(source);
+
+  window.onload = async function() {
+    SimpleTest.waitForExplicitFinish();
+
+    domLocalization.addResourceIds(["/browser/menu.ftl"], 1);
+
+    const frag = document.querySelectorAll("div")[0];
+    const h1 = document.querySelectorAll("h1")[0];
+    const p1 = document.querySelectorAll("p")[0];
+
+    await domLocalization.translateFragment(frag);
+    is(h1.textContent, "Hello World");
+    is(p1.textContent, "Welcome to Fluent");
+
+    // Cleanup
+    L10nRegistry.removeSource(source.name);
+    L10nRegistry.load = originalLoad;
+    Services.locale.setRequestedLocales(originalRequested);
+
+    SimpleTest.finish();
+  };
+  </script>
+</head>
+<body>
+  <div>
+    <h1 data-l10n-id="title" />
+    <p data-l10n-id="subtitle" />
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/intl/l10n/test/dom/test_mozdom_translateRoots.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test mozIDOMLocalization.connectRoot and translateRoots</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <script type="application/javascript">
+  "use strict";
+  const domLocalization =
+    Cc["@mozilla.org/intl/domlocalization;1"].createInstance(
+      Ci.mozIDOMLocalization);
+  const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
+  const { L10nRegistry, FileSource } =
+    ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm", {});
+
+  const fs = {
+    "/localization/en-US/browser/menu.ftl": `
+title = Hello World
+title2 = Hello Another World
+`,
+  };
+  const originalLoad = L10nRegistry.load;
+  const originalRequested = Services.locale.getRequestedLocales();
+
+  L10nRegistry.load = async function(url) {
+    return fs.hasOwnProperty(url) ? fs[url] : false;
+  };
+
+  const source = new FileSource("test", ["en-US"], "/localization/{locale}");
+  L10nRegistry.registerSource(source);
+
+  window.onload = async function() {
+    SimpleTest.waitForExplicitFinish();
+
+    domLocalization.addResourceIds(["/browser/menu.ftl"], 1);
+
+    const frag1 = document.querySelectorAll("div")[0];
+    const frag2 = document.querySelectorAll("div")[1];
+    const h1 = document.querySelectorAll("h1")[0];
+    const h2 = document.querySelectorAll("h2")[0];
+
+    domLocalization.connectRoot(frag1);
+    domLocalization.connectRoot(frag2);
+
+    await domLocalization.translateRoots();
+
+    is(h1.textContent, "Hello World");
+    is(h2.textContent, "Hello Another World");
+
+    // Cleanup
+    L10nRegistry.removeSource(source.name);
+    L10nRegistry.load = originalLoad;
+    Services.locale.setRequestedLocales(originalRequested);
+
+    SimpleTest.finish();
+  };
+  </script>
+</head>
+<body>
+  <div>
+    <h1 data-l10n-id="title"></h1>
+  </div>
+  <div>
+    <h2 data-l10n-id="title2"></h2>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/intl/l10n/test/test_mozdomlocalization.js
@@ -0,0 +1,72 @@
+const domLocalization =
+  Cc["@mozilla.org/intl/domlocalization;1"].createInstance(
+    Ci.mozIDOMLocalization);
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
+const { L10nRegistry, FileSource } =
+  ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm", {});
+
+const fs = {
+  "/localization/en-US/browser/menu.ftl": "key = [en] Value",
+};
+const originalLoad = L10nRegistry.load;
+const originalRequested = Services.locale.getRequestedLocales();
+
+L10nRegistry.load = async function(url) {
+  return fs.hasOwnProperty(url) ? fs[url] : false;
+};
+
+const source = new FileSource("test", ["en-US"], "/localization/{locale}");
+L10nRegistry.registerSource(source);
+
+add_task(function test_methods_presence() {
+  equal(typeof domLocalization.addResourceIds, "function");
+  equal(typeof domLocalization.removeResourceIds, "function");
+
+  equal(typeof domLocalization.formatMessages, "function");
+  equal(typeof domLocalization.formatValues, "function");
+  equal(typeof domLocalization.formatValue, "function");
+
+  equal(typeof domLocalization.translateFragment, "function");
+  equal(typeof domLocalization.translateElements, "function");
+
+  equal(typeof domLocalization.connectRoot, "function");
+  equal(typeof domLocalization.translateRoots, "function");
+
+  equal(typeof domLocalization.ready, "object");
+});
+
+add_task(function test_add_remove_resources() {
+  equal(domLocalization.addResourceIds(["./path1.ftl", "./path2.ftl"], 2), 2);
+  equal(domLocalization.removeResourceIds(["./path1.ftl", "./path2.ftl"], 2), 0);
+});
+
+add_task(async function test_format_messages() {
+  domLocalization.addResourceIds(["/browser/menu.ftl"], 1);
+
+  let msgs = await domLocalization.formatMessages([{"id": "key"}], 1);
+  equal(msgs.length, 1);
+  equal(msgs[0].value, "[en] Value");
+});
+
+add_task(async function test_format_values() {
+  let msgs = await domLocalization.formatValues([{"id": "key"}], 1);
+  equal(msgs.length, 1);
+  equal(msgs[0], "[en] Value");
+});
+
+add_task(async function test_format_value() {
+  let msg = await domLocalization.formatValue("key");
+  equal(msg, "[en] Value");
+});
+
+add_task(async function test_ready() {
+  await domLocalization.ready;
+  equal(1, 1);
+});
+
+add_task(function cleanup() {
+  L10nRegistry.sources.clear();
+  L10nRegistry.load = originalLoad;
+  Services.locale.setRequestedLocales(originalRequested);
+});
--- a/intl/l10n/test/xpcshell.ini
+++ b/intl/l10n/test/xpcshell.ini
@@ -1,8 +1,9 @@
 [DEFAULT]
 head =
 
 [test_domlocalization.js]
 [test_l10nregistry.js]
 [test_localization.js]
 [test_messagecontext.js]
+[test_mozdomlocalization.js]
 [test_pseudo.js]
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -234,16 +234,19 @@
 @BINPATH@/components/PresentationDataChannelSessionTransport.manifest
 @BINPATH@/components/AndroidCastDeviceProvider.manifest
 @BINPATH@/components/AndroidCastDeviceProvider.js
 #endif
 
 @BINPATH@/components/mozIntl.manifest
 @BINPATH@/components/mozIntl.js
 
+@BINPATH@/components/mozDOMLocalization.js
+@BINPATH@/components/mozDOMLocalization.manifest
+
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 @BINPATH@/components/nsURLClassifier.manifest
 @BINPATH@/components/nsUrlClassifierHashCompleter.js
 @BINPATH@/components/nsUrlClassifierListManager.js
 @BINPATH@/components/nsUrlClassifierLib.js