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
--- 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