summaryrefslogtreecommitdiffstats
path: root/dom/l10n/tests/mochitest/document_l10n
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /dom/l10n/tests/mochitest/document_l10n
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/l10n/tests/mochitest/document_l10n')
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/README.txt3
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/non-system-principal/README.txt3
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/non-system-principal/browser_resource_uri.js109
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/non-system-principal/localization/test.ftl4
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/non-system-principal/test.html37
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent.html90
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent_lazy.html98
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/test_docl10n.html66
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/test_docl10n.xhtml60
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/test_docl10n_lazy.html44
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/test_docl10n_ready_rejected.html29
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/test_docl10n_removeResourceIds.html59
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/test_docl10n_sync.html54
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/test_telemetry.html83
-rw-r--r--dom/l10n/tests/mochitest/document_l10n/test_unpriv_iframe.html26
15 files changed, 765 insertions, 0 deletions
diff --git a/dom/l10n/tests/mochitest/document_l10n/README.txt b/dom/l10n/tests/mochitest/document_l10n/README.txt
new file mode 100644
index 0000000000..b798a5039a
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/README.txt
@@ -0,0 +1,3 @@
+Tests in this directory cover support for DocumentL10n
+WebIDL API across different use cases such as
+processes, principals and so on.
diff --git a/dom/l10n/tests/mochitest/document_l10n/non-system-principal/README.txt b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/README.txt
new file mode 100644
index 0000000000..d0cc074166
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/README.txt
@@ -0,0 +1,3 @@
+Tests in this directory cover the functionality
+of DocumentL10n WebIDL API in non-system-principal
+scenario.
diff --git a/dom/l10n/tests/mochitest/document_l10n/non-system-principal/browser_resource_uri.js b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/browser_resource_uri.js
new file mode 100644
index 0000000000..a658e88bec
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/browser_resource_uri.js
@@ -0,0 +1,109 @@
+let uri =
+ "chrome://mochitests/content/browser/dom/l10n/tests/mochitest//document_l10n/non-system-principal/";
+let protocol = Services.io
+ .getProtocolHandler("resource")
+ .QueryInterface(Ci.nsIResProtocolHandler);
+
+protocol.setSubstitution("l10n-test", Services.io.newURI(uri));
+
+// Since we want the mock source to work with all locales, we're going
+// to register it for currently used locales, and we'll put the path that
+// doesn't use the `{locale}` component to make it work irrelevant of
+// what locale the mochitest is running in.
+//
+// Notice: we're using a `chrome://` protocol here only for convenience reasons.
+// Real sources should use `resource://` protocol.
+let locales = Services.locale.appLocalesAsBCP47;
+
+// This source is actually using a real `FileSource` instead of a mocked one,
+// because we want to test that fetching real I/O out of the `uri` works in non-system-principal.
+let source = new L10nFileSource("test", "app", locales, `${uri}localization/`);
+L10nRegistry.getInstance().registerSources([source]);
+
+registerCleanupFunction(() => {
+ protocol.setSubstitution("l10n-test", null);
+ L10nRegistry.getInstance().removeSources(["test"]);
+ SpecialPowers.pushPrefEnv({
+ set: [["dom.ipc.processPrelaunch.enabled", true]],
+ });
+});
+
+const kChildPage = getRootDirectory(gTestPath) + "test.html";
+
+const kAboutPagesRegistered = Promise.all([
+ BrowserTestUtils.registerAboutPage(
+ registerCleanupFunction,
+ "test-about-l10n-child",
+ kChildPage,
+ Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD |
+ Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT |
+ Ci.nsIAboutModule.ALLOW_SCRIPT
+ ),
+]);
+
+add_task(async () => {
+ // Bug 1640333 - windows fails (sometimes) to ever get document.l10n.ready
+ // if e10s process caching is enabled
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.ipc.processPrelaunch.enabled", false],
+ ["dom.security.skip_about_page_has_csp_assert", true],
+ ],
+ });
+ await kAboutPagesRegistered;
+ await BrowserTestUtils.withNewTab(
+ "about:test-about-l10n-child",
+ async browser => {
+ await SpecialPowers.spawn(browser, [], async function () {
+ let document = content.document;
+ let window = document.defaultView;
+
+ await document.testsReadyPromise;
+
+ let principal = SpecialPowers.wrap(document).nodePrincipal;
+ is(
+ principal.spec,
+ "about:test-about-l10n-child",
+ "correct content principal"
+ );
+
+ let desc = document.getElementById("main-desc");
+
+ // We can test here for a particular value because we're
+ // using a mock file source which is locale independent.
+ //
+ // If you're writing a test that verifies that a UI
+ // widget got real localization, you should not rely on
+ // the particular value, but rather on the content not
+ // being empty (to keep the test pass in non-en-US locales).
+ is(desc.textContent, "This is a mock page title");
+
+ // Test for l10n.getAttributes
+ let label = document.getElementById("label1");
+ let l10nArgs = document.l10n.getAttributes(label);
+ is(l10nArgs.id, "subtitle");
+ is(l10nArgs.args.name, "Firefox");
+
+ // Test for manual value formatting
+ let customMsg = document.getElementById("customMessage").textContent;
+ is(customMsg, "This is a custom message formatted from JS.");
+
+ // Since we applied the `data-l10n-id` attribute
+ // on `label` in this microtask, we have to wait for
+ // the next paint to verify that the MutationObserver
+ // applied the translation.
+ await new Promise(resolve => {
+ let verifyL10n = () => {
+ if (!label.textContent.includes("Firefox")) {
+ window.requestAnimationFrame(verifyL10n);
+ } else {
+ resolve();
+ }
+ };
+
+ window.requestAnimationFrame(verifyL10n);
+ });
+ });
+ }
+ );
+});
diff --git a/dom/l10n/tests/mochitest/document_l10n/non-system-principal/localization/test.ftl b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/localization/test.ftl
new file mode 100644
index 0000000000..a5da5a8f00
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/localization/test.ftl
@@ -0,0 +1,4 @@
+page-title = This is a mock page title
+subtitle = This is a label for { $name }
+
+custom-message = This is a custom message formatted from JS.
diff --git a/dom/l10n/tests/mochitest/document_l10n/non-system-principal/test.html b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/test.html
new file mode 100644
index 0000000000..5d91f3da46
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/non-system-principal/test.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test DocumentL10n in HTML environment</title>
+ <link rel="localization" href="test.ftl"/>
+ <script type="text/javascript">
+ document.testsReadyPromise = new Promise((resolve) => {
+ // The test is in this file to ensure that we're testing
+ // the behavior in a non-system principal.
+ document.addEventListener("DOMContentLoaded", async () => {
+ await document.l10n.ready;
+
+ // Assign the localization from JS
+ let label = document.getElementById("label1");
+ document.l10n.setAttributes(
+ label,
+ "subtitle",
+ {
+ name: "Firefox",
+ }
+ );
+
+ const customMsg = await document.l10n.formatValue("custom-message");
+ document.getElementById("customMessage").textContent = customMsg;
+ resolve();
+ }, {once: true});
+ });
+ </script>
+</head>
+<body>
+ <h1 id="main-desc" data-l10n-id="page-title"></h1>
+
+ <p id="label1"></p>
+ <p id="customMessage"></p>
+</body>
+</html>
diff --git a/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent.html b/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent.html
new file mode 100644
index 0000000000..3f2def3547
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test Web Component connecting into Document's l10n</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="localization" href="browser/preferences/preferences.ftl"></link>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ // In this test we are introducing two widgets. The only difference between them
+ // is that the first one is using `connectRoot` with the `aTranslate` argument set to `true`,
+ // and the other one to `false`.
+ //
+ // In this test, we will inject both of them into the DOM for parsing.
+ // For a test that verifies the behavior when they're injected lazily, see
+ // `test_connectRoot_webcomponent_lazy.html` test.
+ //
+ // Since both widgets get injected into DOM during parsing, we expect both of them
+ // to get translated before `document.l10n.ready` is resolved.
+
+ let passedTests = 0;
+
+ class FluentWidget extends HTMLElement {
+ constructor() {
+ super();
+ const shadowRoot = this.attachShadow({mode: "open"});
+ const t = document.querySelector("#fluent-widget-template");
+ const instance = t.content.cloneNode(true);
+ shadowRoot.appendChild(instance);
+ }
+ async connectedCallback() {
+ MozXULElement.insertFTLIfNeeded("browser/preferences/preferences.ftl");
+
+ document.l10n.connectRoot(this.shadowRoot, true);
+
+ let label = this.shadowRoot.getElementById("label");
+
+ await document.l10n.ready;
+ is(label.textContent, "Learn more", "localization content applied to element");
+ passedTests++;
+ if (passedTests == 2) {
+ SimpleTest.finish();
+ }
+ }
+ }
+
+ class FluentWidget2 extends HTMLElement {
+ constructor() {
+ super();
+ const shadowRoot = this.attachShadow({mode: "open"});
+ const t = document.querySelector("#fluent-widget-template");
+ const instance = t.content.cloneNode(true);
+ shadowRoot.appendChild(instance);
+ }
+ async connectedCallback() {
+ MozXULElement.insertFTLIfNeeded("browser/preferences/preferences.ftl");
+
+ document.l10n.connectRoot(this.shadowRoot, false);
+
+ let label = this.shadowRoot.getElementById("label");
+
+ await document.l10n.ready;
+ is(label.textContent, "Learn more", "localization content applied to element");
+ passedTests++;
+ if (passedTests == 2) {
+ SimpleTest.finish();
+ }
+ }
+ }
+
+ customElements.define("fluent-widget", FluentWidget);
+ customElements.define("fluent-widget2", FluentWidget2);
+ </script>
+</head>
+<body>
+ <template id="fluent-widget-template">
+ <div>
+ <button id="label" data-l10n-id="do-not-track-learn-more"></button>
+ </div>
+ </template>
+ <fluent-widget></fluent-widget>
+ <fluent-widget2></fluent-widget2>
+ <script>
+ // This trick makes sure that we connect the widgets before parsing is completed.
+ document.write("");
+ </script>
+</body>
+</html>
diff --git a/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent_lazy.html b/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent_lazy.html
new file mode 100644
index 0000000000..b74bbc00c8
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/test_connectRoot_webcomponent_lazy.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test Web Component connecting into Document's l10n</title>
+ <script 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">
+ SimpleTest.waitForExplicitFinish();
+
+ // In this test we are introducing two widgets. The only difference between them
+ // is that the first one is using `connectRoot` with the `aTranslate` argument set to `true`,
+ // and the other one to `false`.
+ //
+ // In this test, we will inject both of them lazily, after initial parsing is completed.
+ // For a test that verifies the behavior when they're injected during parsing, see
+ // `test_connectRoot_webcomponent.html` test.
+ //
+ // The expected difference is that when both get lazily injected into the DOM, the first one
+ // will get translated, while the other will not.
+ // The latter behavior will be used by widgets that will want to translate the initial DOM on their
+ // own before connecting the root.
+
+ let firstWidgetTranslated = false;
+
+ class FluentWidget extends HTMLElement {
+ constructor() {
+ super();
+ const shadowRoot = this.attachShadow({mode: "open"});
+ const t = document.querySelector("#fluent-widget-template");
+ const instance = t.content.cloneNode(true);
+ shadowRoot.appendChild(instance);
+ }
+ connectedCallback() {
+ MozXULElement.insertFTLIfNeeded("browser/preferences/preferences.ftl");
+
+ document.l10n.connectRoot(this.shadowRoot, true);
+
+ let label = this.shadowRoot.getElementById("label");
+
+ let verifyL10n = () => {
+ if (label.textContent.length) {
+ window.removeEventListener("MozAfterPaint", verifyL10n);
+ is(label.textContent, "Learn more", "localization content applied to element");
+ firstWidgetTranslated = true;
+ }
+ };
+ window.addEventListener("MozAfterPaint", verifyL10n);
+ }
+ }
+
+ class FluentWidget2 extends HTMLElement {
+ constructor() {
+ super();
+ const shadowRoot = this.attachShadow({mode: "open"});
+ const t = document.querySelector("#fluent-widget-template");
+ const instance = t.content.cloneNode(true);
+ shadowRoot.appendChild(instance);
+ }
+ connectedCallback() {
+ MozXULElement.insertFTLIfNeeded("browser/preferences/preferences.ftl");
+
+ document.l10n.connectRoot(this.shadowRoot, false);
+
+ let label = this.shadowRoot.getElementById("label");
+
+ let verifyL10n = () => {
+ if (firstWidgetTranslated) {
+ is(label.textContent.length, 0, "This widget should remain untranslated.");
+ window.removeEventListener("MozAfterPaint", verifyL10n);
+ SimpleTest.finish();
+ }
+ };
+ window.addEventListener("MozAfterPaint", verifyL10n);
+ }
+ }
+
+ customElements.define("fluent-widget", FluentWidget);
+ customElements.define("fluent-widget2", FluentWidget2);
+
+ window.addEventListener("load", () => {
+ window.requestIdleCallback(async () => {
+ let widget = document.createElement("fluent-widget");
+ document.body.appendChild(widget);
+ let widget2 = document.createElement("fluent-widget2");
+ document.body.appendChild(widget2);
+ });
+ }, { once: true });
+ </script>
+</head>
+<body>
+ <template id="fluent-widget-template">
+ <div>
+ <button id="label" data-l10n-id="do-not-track-learn-more"></button>
+ </div>
+ </template>
+</body>
+</html>
diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n.html b/dom/l10n/tests/mochitest/document_l10n/test_docl10n.html
new file mode 100644
index 0000000000..12ff623f5c
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test DocumentL10n in HTML environment</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="localization" href="crashreporter/aboutcrashes.ftl"/>
+ <script>
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ is(document.l10n.ready && document.l10n.ready.then !== undefined, true,
+ "document.l10n.ready exists and is a Promise");
+
+ (async function() {
+ await document.l10n.ready;
+
+ const desc = document.getElementById("main-desc");
+ is(!!desc.textContent.length, true, "initial localization is applied");
+
+ const msg = await document.l10n.formatValue("id-heading");
+ is(!!msg.length, true, "value is formatted manually");
+
+ const label = document.getElementById("label1");
+ let l10nArgs = document.l10n.getAttributes(label);
+ is(l10nArgs.id, null, "id is null if not set");
+
+ SimpleTest.doesThrow(
+ () => {
+ const bad = {};
+ bad.bad = bad;
+ document.l10n.setAttributes(label, "date-crashed-heading", bad);
+ },
+ "an error is thrown for invalid args",
+ );
+
+ l10nArgs = document.l10n.getAttributes(label);
+ is(l10nArgs.id, null, "id is not set if args are invalid");
+
+ document.l10n.setAttributes(
+ label,
+ "date-crashed-heading",
+ {
+ name: "John",
+ }
+ );
+ ok(document.hasPendingL10nMutations, "Should have pending mutations");
+ l10nArgs = document.l10n.getAttributes(label);
+ is(l10nArgs.id, "date-crashed-heading", "id is set by setAttributes");
+ is(l10nArgs.args.name, "John", "args are set by setAttributes");
+ // Test for mutations applied.
+ document.addEventListener("L10nMutationsFinished", function() {
+ ok(!!label.textContent.length, "Should've applied translation");
+ ok(!document.hasPendingL10nMutations, "Should have no more pending mutations");
+ SimpleTest.finish();
+ }, { once: true });
+ })();
+ </script>
+</head>
+<body>
+ <h1 id="main-desc" data-l10n-id="crash-reports-title"></h1>
+
+ <p id="label1"></p>
+</body>
+</html>
diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n.xhtml b/dom/l10n/tests/mochitest/document_l10n/test_docl10n.xhtml
new file mode 100644
index 0000000000..2d51d8689e
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n.xhtml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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/. -->
+
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta charset="utf-8"></meta>
+ <title>Test DocumentL10n in HTML environment</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"></link>
+ <link rel="localization" href="crashreporter/aboutcrashes.ftl"/>
+ <script type="application/javascript">
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ document.addEventListener("DOMContentLoaded", async function() {
+ await document.l10n.ready;
+
+ // Test for initial localization applied.
+ let desc = document.getElementById("main-desc");
+ is(!!desc.textContent.length, true);
+
+ // Test for manual value formatting
+ let msg = await document.l10n.formatValue("id-heading");
+ is(!!msg.length, true);
+
+ // Test for mutations applied.
+ let verifyL10n = () => {
+ if (label.textContent.length) {
+ window.removeEventListener("MozAfterPaint", verifyL10n);
+ SimpleTest.finish();
+ }
+ };
+ window.addEventListener("MozAfterPaint", verifyL10n);
+
+ let label = document.getElementById("label1");
+ document.l10n.setAttributes(
+ label,
+ "date-crashed-heading",
+ {
+ name: "John",
+ }
+ );
+
+ // Test for l10n.getAttributes
+ let l10nArgs = document.l10n.getAttributes(label);
+ is(l10nArgs.id, "date-crashed-heading");
+ is(l10nArgs.args.name, "John");
+ }, { once: true});
+ </script>
+</head>
+<body>
+ <h1 id="main-desc" data-l10n-id="crash-reports-title"></h1>
+
+ <p id="label1" />
+</body>
+</html>
diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n_lazy.html b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_lazy.html
new file mode 100644
index 0000000000..6c3ddb73ed
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_lazy.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test Lazy DocumentL10n in HTML environment</title>
+ <script 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";
+ SimpleTest.waitForExplicitFinish();
+
+ is(document.l10n, null, "document.l10n is null.");
+
+ window.addEventListener("load", async () => {
+ is(document.l10n, null, "document.l10n is null after load.");
+
+ let desc = document.getElementById("main-desc");
+ is(desc.textContent.length, 0, "main-desc is not translated");
+
+ let link = document.createElement("link");
+ link.setAttribute("rel", "localization");
+ link.setAttribute("href", "crashreporter/aboutcrashes.ftl");
+ document.head.appendChild(link);
+
+ // Verify now that `l10n.ready` exists and is fulfilled.
+ await document.l10n.ready;
+
+ // Lazy initialized localization should translate the document.
+ is(!!desc.textContent.length, true, "main-desc is translated");
+
+ document.head.removeChild(link);
+
+ is(document.l10n, null, "document.l10n is null");
+
+ SimpleTest.finish();
+ }, { once: true});
+ </script>
+</head>
+<body>
+ <h1 id="main-desc" data-l10n-id="crash-reports-title"></h1>
+
+ <p id="label1"></p>
+</body>
+</html>
diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n_ready_rejected.html b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_ready_rejected.html
new file mode 100644
index 0000000000..63e18f802c
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_ready_rejected.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test mozIDOMLocalization.ready rejected state</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="localization" href="path/to_non_existing.ftl"/>
+ <script type="application/javascript">
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ document.addEventListener("DOMContentLoaded", async function() {
+ /**
+ * Even when we fail to localize all elements, we will
+ * still resolve the `ready` promise to communicate that
+ * the initial translation phase is now completed.
+ */
+ document.l10n.ready.then(() => {
+ is(1, 1, "the ready should resolve");
+ SimpleTest.finish();
+ });
+ });
+ </script>
+</head>
+<body>
+ <h1 data-l10n-id="non-existing-id"></h1>
+</body>
+</html>
diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n_removeResourceIds.html b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_removeResourceIds.html
new file mode 100644
index 0000000000..8ccaa04614
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_removeResourceIds.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test DocumentL10n::RemoveResourceIds</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="localization" href="toolkit/about/aboutAddons.ftl"/>
+ <link rel="localization" href="toolkit/about/aboutSupport.ftl"/>
+ <script type="application/javascript">
+ "use strict";
+ /* eslint-disable mozilla/prefer-formatValues */
+
+ SimpleTest.waitForExplicitFinish();
+
+ window.onload = async function() {
+ {
+ // 1. An example message from aboutAddons should be available.
+ let value = await document.l10n.formatValue("shortcuts-browserAction2");
+ is(!!value.length, true, "localized value retrieved");
+ }
+
+ {
+ // 2. Remove aboutAddons.ftl
+ let link = document.head.querySelector("link[href*=aboutAddons]");
+ document.head.removeChild(link);
+ }
+
+ {
+ // 3. An example message from aboutSupport should still be available.
+ let value = await document.l10n.formatValue("features-version");
+ is(!!value.length, true, "localized value retrieved");
+
+ // 4. An example message from aboutAddons should not be available.
+ await document.l10n.formatValue("shortcuts-browserAction").then(
+ () => {
+ ok(false, "localization should not be available");
+ },
+ () => {
+ ok(true, "localization should not be available");
+ });
+ }
+
+ {
+ // 5. Remove aboutSupport.ftl
+ let link = document.head.querySelector("link[href*=aboutSupport]");
+ document.head.removeChild(link);
+
+ // 6. document.l10n should be null.
+ is(document.l10n, null, "document.l10n should be null");
+
+ SimpleTest.finish();
+ }
+ };
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/dom/l10n/tests/mochitest/document_l10n/test_docl10n_sync.html b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_sync.html
new file mode 100644
index 0000000000..ea44d1afe1
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/test_docl10n_sync.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html data-l10n-sync>
+<head>
+ <meta charset="utf-8">
+ <title>Test DocumentL10n in HTML environment</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="localization" href="crashreporter/aboutcrashes.ftl"/>
+ <script type="application/javascript">
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ document.addEventListener("DOMContentLoaded", async function() {
+ await document.l10n.ready;
+
+ // Test for initial localization applied.
+ let desc = document.getElementById("main-desc");
+ is(!!desc.textContent.length, true);
+
+ // Test for manual value formatting.
+ let msg = await document.l10n.formatValue("id-heading");
+ is(!!msg.length, true);
+
+ // Test for mutations applied.
+ let verifyL10n = () => {
+ if (label.textContent.length) {
+ window.removeEventListener("MozAfterPaint", verifyL10n);
+ SimpleTest.finish();
+ }
+ };
+ window.addEventListener("MozAfterPaint", verifyL10n);
+
+ let label = document.getElementById("label1");
+ document.l10n.setAttributes(
+ label,
+ "date-crashed-heading",
+ {
+ name: "John",
+ }
+ );
+
+ // Test for l10n.getAttributes
+ let l10nArgs = document.l10n.getAttributes(label);
+ is(l10nArgs.id, "date-crashed-heading");
+ is(l10nArgs.args.name, "John");
+ }, { once: true});
+ </script>
+</head>
+<body>
+ <h1 id="main-desc" data-l10n-id="crash-reports-title"></h1>
+
+ <p id="label1"></p>
+</body>
+</html>
diff --git a/dom/l10n/tests/mochitest/document_l10n/test_telemetry.html b/dom/l10n/tests/mochitest/document_l10n/test_telemetry.html
new file mode 100644
index 0000000000..b528ebc7ea
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/test_telemetry.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test DocumentL10n Telemetry</title>
+ <script 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 { BrowserTestUtils } = ChromeUtils.import(
+ "resource://testing-common/BrowserTestUtils.jsm"
+ );
+
+ SimpleTest.waitForExplicitFinish();
+
+ function countValues(snapshot, key) {
+ if (!snapshot.hasOwnProperty(key)) {
+ return 0;
+ }
+ let values = snapshot[key].values;
+ return Object.values(values).reduce((sum, n) => sum + n, 0);
+ }
+
+ (async function() {
+ let histogram = Services.telemetry
+ .getKeyedHistogramById("L10N_DOCUMENT_INITIAL_TRANSLATION_TIME_US");
+ let snapshot = histogram.snapshot();
+
+ // In some cases the test runs before first window is localized.
+ // We just want to ensure that we didn't register more than 1
+ // first window telemetry.
+ let firstWindowCount = countValues(snapshot, "browser_first_window");
+ is(firstWindowCount < 2, true);
+
+ histogram.clear();
+
+ // Open a new window
+ let win = await BrowserTestUtils.openNewBrowserWindow({
+ waitForTabURL: "about:blank",
+ });
+
+ // Telemetry in testing is flaky and when landing this test
+ // we saw cases where the snapshot did not contain the new
+ // window telemetry at this moment.
+ //
+ // We're going to use `waitForCondition` to test for
+ // the telemetry to be eventually recorded.
+ BrowserTestUtils.waitForCondition(() => {
+ snapshot = histogram.snapshot();
+
+ // We want to make sure that since we cleared histogram
+ // just one new window of either type has been opened.
+ let browserWindowsCount =
+ countValues(snapshot, "browser_new_window") +
+ countValues(snapshot, "browser_first_window");
+ return browserWindowsCount == 1;
+ });
+
+ // Open preferences in a new tab
+ let tab = BrowserTestUtils.addTab(
+ win.gBrowser,
+ "about:preferences"
+ );
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ // Similarly to the above, we've seen cases where the telemetry
+ // was not present right after the tab was opened, so
+ // we'll use `waitForCondition` here.
+ BrowserTestUtils.waitForCondition(() => {
+ snapshot = histogram.snapshot();
+
+ return countValues(snapshot, "about:preferences") == 1;
+ });
+
+ await BrowserTestUtils.closeWindow(win);
+
+ SimpleTest.finish();
+ })();
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/dom/l10n/tests/mochitest/document_l10n/test_unpriv_iframe.html b/dom/l10n/tests/mochitest/document_l10n/test_unpriv_iframe.html
new file mode 100644
index 0000000000..4f4b29c500
--- /dev/null
+++ b/dom/l10n/tests/mochitest/document_l10n/test_unpriv_iframe.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Ensure unprivilaged document cannot access document.l10n in an iframe</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="application/javascript">
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ addLoadEvent(function() {
+ let frame = document.getElementById("frame");
+ let frame2 = document.getElementById("frame2");
+
+ is("l10n" in frame.contentDocument, false);
+ is("l10n" in frame2.contentDocument, false);
+ });
+ addLoadEvent(SimpleTest.finish);
+ </script>
+</head>
+<body>
+ <iframe id="frame" src="about:blank"></iframe>
+ <iframe id="frame2" src="about:crashes"></iframe>
+</body>
+</html>