commit 6e55eaa041e15b85df9afde96af7d139e6730024
parent d31739e1c0e3859874d62909f4bc5314de12ff3a
Author: Erik Nordin <enordin@mozilla.com>
Date: Wed, 1 Oct 2025 16:07:08 +0000
Bug 1991224 - Resize about:translations textareas on window resize and zoom r=translations-reviewers,gregtatum
This patch allows the `about:translations` page's <textarea>
elements to resize themselves if resizing the window or changing
the zoom level would cause the content to expand beyond the
current boundary of the text area.
Differential Revision: https://phabricator.services.mozilla.com/D266489
Diffstat:
6 files changed, 632 insertions(+), 7 deletions(-)
diff --git a/toolkit/components/translations/content/about-translations.mjs b/toolkit/components/translations/content/about-translations.mjs
@@ -95,6 +95,36 @@ class AboutTranslations {
#readyPromiseWithResolvers = Promise.withResolvers();
/**
+ * The orientation of the page's content.
+ *
+ * When the page orientation is horizontal the source and target text areas
+ * are displayed side by side with the source text area at the inline start
+ * and the target text area at the inline end.
+ *
+ * When the page orientation is vertical the source text area is displayed
+ * above the target text area, independent of locale bidirectionality.
+ *
+ * When the page orientation is vertical, each text area spans the full width
+ * of the content, and resizing the window width or changing the zoom level
+ * must trigger text-area resizing, where as resizing the text areas is not
+ * necessary for these scenarios when the page orientation is horizontal.
+ *
+ * @type {("vertical"|"horizontal")}
+ */
+ #pageOrientation = "horizontal";
+
+ /**
+ * A timeout id that gets set when a pending callback is scheduled
+ * to synchronize the heights of the source and target text areas.
+ *
+ * This helps ensure that we do not make repeated calls to this function
+ * that would cause unnecessary and excessive reflow.
+ *
+ * @type {number | null}
+ */
+ #synchronizeTextAreaHeightsTimeoutId = null;
+
+ /**
* Constructs a new {@link AboutTranslations} instance.
*
* @param {boolean} isTranslationsEngineSupported
@@ -191,12 +221,41 @@ class AboutTranslations {
}
/**
+ * Sets the internal data member for which orientation the page is in.
+ *
+ * This information is important for performance regarding in which situations
+ * we need to dynamically resize the <textarea> elements on the page.
+ *
+ * @returns {boolean} True if the page orientation changed, otherwise false.
+ */
+ #updatePageOrientation() {
+ const orientationAtStart = this.#pageOrientation;
+
+ // Keep this value up to date with the media query rule in about-translations.css
+ this.#pageOrientation =
+ window.innerWidth > 1200 ? "horizontal" : "vertical";
+
+ const orientationChanged = orientationAtStart !== this.#pageOrientation;
+
+ if (orientationChanged) {
+ document.dispatchEvent(
+ new CustomEvent("AboutTranslations:PageOrientationChanged", {
+ detail: { orientation: this.#pageOrientation },
+ })
+ );
+ }
+
+ return orientationChanged;
+ }
+
+ /**
* An async setup function to initialize the about:translations page.
*
* Performs all initialization steps and finally reveals the main UI.
*/
async #setup() {
this.#initializeEventListeners();
+ this.#updatePageOrientation();
this.#translatingPlaceholderText = await document.l10n.formatValue(
"about-translations-translating-message"
@@ -271,6 +330,8 @@ class AboutTranslations {
sourceLanguageSelector.addEventListener("input", this);
targetLanguageSelector.addEventListener("input", this);
sourceTextArea.addEventListener("input", this);
+ window.addEventListener("resize", this);
+ window.visualViewport.addEventListener("resize", this);
}
/**
@@ -329,6 +390,21 @@ class AboutTranslations {
break;
}
+ case "resize": {
+ if (target === window || target === window.visualViewport) {
+ const orientationChanged = this.#updatePageOrientation();
+
+ if (orientationChanged) {
+ // The page orientation changed, so we need to update the text-area heights immediately.
+ this.#ensureTextAreaHeightsMatch({ scheduleCallback: false });
+ } else if (this.#pageOrientation === "vertical") {
+ // Otherwise we only need to eventually update the text-area heights in vertical orientation.
+ this.#ensureTextAreaHeightsMatch({ scheduleCallback: true });
+ }
+ }
+
+ break;
+ }
}
};
@@ -633,7 +709,7 @@ class AboutTranslations {
this.#updateSourceScriptDirection();
this.#updateTargetScriptDirection();
- this.#synchronizeTextAreasToMaxContentHeight();
+ this.#ensureTextAreaHeightsMatch({ scheduleCallback: false });
if (sourceTextArea.value) {
this.#displayTranslatingPlaceholder();
@@ -693,7 +769,7 @@ class AboutTranslations {
sourceTextArea.dispatchEvent(new Event("input"));
this.#updateSourceScriptDirection();
- this.#synchronizeTextAreasToMaxContentHeight();
+ this.#ensureTextAreaHeightsMatch({ scheduleCallback: false });
}
/**
@@ -711,7 +787,7 @@ class AboutTranslations {
}
this.#updateTargetScriptDirection();
- this.#synchronizeTextAreasToMaxContentHeight();
+ this.#ensureTextAreaHeightsMatch({ scheduleCallback: false });
}
/**
@@ -955,7 +1031,7 @@ class AboutTranslations {
onDebounce: async () => {
try {
this.#updateURLFromUI();
- this.#synchronizeTextAreasToMaxContentHeight();
+ this.#ensureTextAreaHeightsMatch({ scheduleCallback: false });
await this.#maybeUpdateDetectedSourceLanguage();
@@ -1047,10 +1123,58 @@ class AboutTranslations {
});
/**
+ * Ensures that the heights of the source and target text areas match by syncing
+ * them to the maximum height of either of their content.
+ *
+ * There are many situations in which this function needs to be called:
+ * - Every time the source text is updated
+ * - Every time a translation occurs
+ * - Every time the window is resized
+ * - Every time the zoom level is changed
+ * - Etc.
+ *
+ * Some of these events happen infrequently, or are already debounced, such as
+ * each time a translation occurs. In these situations it is okay to synchronize
+ * the text-area heights immediately.
+ *
+ * Some of these events can trigger quite rapidly, such as resizing the window
+ * via click-and-drag semantics. In this case, a single callback should be scheduled
+ * to synchronize the text-area heights to prevent unnecessary and excessive reflow.
+ *
+ * @param {object} params
+ * @param {boolean} params.scheduleCallback
+ */
+ #ensureTextAreaHeightsMatch({ scheduleCallback }) {
+ if (scheduleCallback) {
+ if (this.#synchronizeTextAreaHeightsTimeoutId) {
+ // There is already a pending callback: no need to schedule another.
+ return;
+ }
+
+ this.#synchronizeTextAreaHeightsTimeoutId = setTimeout(
+ this.#synchronizeTextAreasToMaxContentHeight,
+ 100
+ );
+
+ return;
+ }
+
+ this.#synchronizeTextAreasToMaxContentHeight();
+ }
+
+ /**
* Calculates the heights of the content in both the source and target text areas,
* then syncs them both to the maximum calculated content height among the two.
+ *
+ * This function is intentionally written as a lambda so that it can be passed
+ * as a callback without the need to explicitly bind `this` to the function object.
+ *
+ * Prefer calling #ensureTextAreaHeightsMatch to make it clear whether this function
+ * needs to run immediately, or is okay to be scheduled as a callback.
+ *
+ * @see {AboutTranslations#ensureTextAreaHeightsMatch}
*/
- #synchronizeTextAreasToMaxContentHeight() {
+ #synchronizeTextAreasToMaxContentHeight = () => {
const { sourceTextArea, targetTextArea } = this.elements;
// This will be the same for both the source and target text areas.
@@ -1086,7 +1210,9 @@ class AboutTranslations {
})
);
}
- }
+
+ this.#synchronizeTextAreaHeightsTimeoutId = null;
+ };
}
/**
diff --git a/toolkit/components/translations/tests/browser/browser.toml b/toolkit/components/translations/tests/browser/browser.toml
@@ -43,6 +43,10 @@ skip-if = ["os == 'linux'"] # Bug 1821461
["browser_about_translations_textarea_resize_by_input.js"]
+["browser_about_translations_textarea_resize_by_window.js"]
+
+["browser_about_translations_textarea_resize_by_zoom.js"]
+
["browser_about_translations_url_load.js"]
["browser_about_translations_url_update.js"]
diff --git a/toolkit/components/translations/tests/browser/browser_about_translations_textarea_resize_by_input.js b/toolkit/components/translations/tests/browser/browser_about_translations_textarea_resize_by_input.js
@@ -49,7 +49,10 @@ add_task(async function test_about_translations_no_resize_for_small_input() {
],
[AboutTranslationsTestUtils.Events.ShowTranslatingPlaceholder],
],
- unexpected: [AboutTranslationsTestUtils.Events.TextAreaHeightsChanged],
+ unexpected: [
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ ],
},
async () => {
await aboutTranslationsTestUtils.setSourceLanguageSelectorValue("de");
@@ -114,6 +117,7 @@ add_task(async function test_about_translations_resize_by_input() {
},
],
],
+ unexpected: [AboutTranslationsTestUtils.Events.PageOrientationChanged],
},
async () => {
await aboutTranslationsTestUtils.setSourceLanguageSelectorValue("de");
@@ -141,6 +145,7 @@ add_task(async function test_about_translations_resize_by_input() {
},
],
],
+ unexpected: [AboutTranslationsTestUtils.Events.PageOrientationChanged],
},
async () => {
await aboutTranslationsTestUtils.resolveDownloads(1);
@@ -175,6 +180,7 @@ add_task(async function test_about_translations_resize_by_input() {
],
],
unexpected: [
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
AboutTranslationsTestUtils.Events.ShowTranslatingPlaceholder,
],
},
@@ -206,6 +212,7 @@ add_task(async function test_about_translations_resize_by_input() {
],
unexpected: [
AboutTranslationsTestUtils.Events.TranslationRequested,
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
AboutTranslationsTestUtils.Events.ShowTranslatingPlaceholder,
],
},
diff --git a/toolkit/components/translations/tests/browser/browser_about_translations_textarea_resize_by_window.js b/toolkit/components/translations/tests/browser/browser_about_translations_textarea_resize_by_window.js
@@ -0,0 +1,236 @@
+/* Any copyright is dedicated to the Public Domain.
+ https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// The German lower-case character "ß" expands to two characters "SS" when capitalized.
+// Our mock translator deterministically capitalizes text for integration tests.
+const expandingInput = `\
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß`;
+
+/**
+ * This test case ensures that modifying the window width within the page's horizontal
+ * orientation does not cause the text areas to resize.
+ */
+add_task(
+ async function test_about_translations_horizontal_orientation_resizes() {
+ const { aboutTranslationsTestUtils, cleanup } = await openAboutTranslations(
+ {
+ languagePairs: [
+ { fromLang: "de", toLang: "en" },
+ { fromLang: "en", toLang: "de" },
+ ],
+ autoDownloadFromRemoteSettings: true,
+ }
+ );
+
+ info(
+ "The text areas should expand when a large input is pasted as the source."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.TranslationRequested,
+ { translationId: 1 },
+ ],
+ [AboutTranslationsTestUtils.Events.ShowTranslatingPlaceholder],
+ [
+ AboutTranslationsTestUtils.Events.TranslationComplete,
+ { translationId: 1 },
+ ],
+ [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ {
+ textAreaHeights: "increased",
+ },
+ ],
+ ],
+ unexpected: [AboutTranslationsTestUtils.Events.PageOrientationChanged],
+ },
+ async () => {
+ await aboutTranslationsTestUtils.setSourceLanguageSelectorValue("de");
+ await aboutTranslationsTestUtils.setTargetLanguageSelectorValue("en");
+ await aboutTranslationsTestUtils.setSourceTextAreaValue(expandingInput);
+ }
+ );
+
+ await aboutTranslationsTestUtils.assertTranslatedText({
+ sourceLanguage: "de",
+ targetLanguage: "en",
+ sourceText: expandingInput,
+ });
+
+ info(
+ "The text area height should not change when the horizontal orientation is made wider, but remains horizontal."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ unexpected: [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
+ ],
+ },
+ async () => {
+ await ensureWindowSize(window, 1500 * Math.SQRT1_2, 900 * Math.SQRT1_2);
+ }
+ );
+
+ info(
+ "The text area height should not change when the horizontal orientation is made narrower, but remains horizontal."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ unexpected: [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
+ ],
+ },
+ async () => {
+ await ensureWindowSize(window, 1600 * Math.SQRT1_2, 900 * Math.SQRT1_2);
+ }
+ );
+
+ await cleanup();
+ }
+);
+
+/**
+ * This test case ensures that transitioning the page between its vertical and horizontal orientations via modifying
+ * the window width may cause the text areas to resize, and that modifying the window width within the page's vertical
+ * orientation may cause the text areas to resize.
+ */
+add_task(async function test_about_translations_vertical_orientation_resizes() {
+ const { aboutTranslationsTestUtils, cleanup } = await openAboutTranslations({
+ languagePairs: [
+ { fromLang: "de", toLang: "en" },
+ { fromLang: "en", toLang: "de" },
+ ],
+ autoDownloadFromRemoteSettings: true,
+ });
+
+ info(
+ "The text-area heights should not change when transitioning to a vertical orientation with no content."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
+ { orientation: "vertical" },
+ ],
+ ],
+ unexpected: [AboutTranslationsTestUtils.Events.TextAreaHeightsChanged],
+ },
+ async () => {
+ await ensureWindowSize(window, 1000 * Math.SQRT1_2, 900 * Math.SQRT1_2);
+ }
+ );
+
+ info(
+ "The text areas should expand when a large input is pasted as the source."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.TranslationRequested,
+ { translationId: 1 },
+ ],
+ [AboutTranslationsTestUtils.Events.ShowTranslatingPlaceholder],
+ [
+ AboutTranslationsTestUtils.Events.TranslationComplete,
+ { translationId: 1 },
+ ],
+ [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ {
+ textAreaHeights: "increased",
+ },
+ ],
+ ],
+ unexpected: [AboutTranslationsTestUtils.Events.PageOrientationChanged],
+ },
+ async () => {
+ await aboutTranslationsTestUtils.setSourceLanguageSelectorValue("de");
+ await aboutTranslationsTestUtils.setTargetLanguageSelectorValue("en");
+ await aboutTranslationsTestUtils.setSourceTextAreaValue(expandingInput);
+ }
+ );
+
+ await aboutTranslationsTestUtils.assertTranslatedText({
+ sourceLanguage: "de",
+ targetLanguage: "en",
+ sourceText: expandingInput,
+ });
+
+ info(
+ "The text-area heights should increase when making the vertical orientation narrower."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ { textAreaHeights: "increased" },
+ ],
+ ],
+ unexpected: [AboutTranslationsTestUtils.Events.PageOrientationChanged],
+ },
+ async () => {
+ await ensureWindowSize(window, 480 * Math.SQRT1_2, 900 * Math.SQRT1_2);
+ }
+ );
+
+ info(
+ "The text-area heights should decrease when making the vertical orientation wider."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ { textAreaHeights: "decreased" },
+ ],
+ ],
+ unexpected: [AboutTranslationsTestUtils.Events.PageOrientationChanged],
+ },
+ async () => {
+ await ensureWindowSize(window, 1000 * Math.SQRT1_2, 900 * Math.SQRT1_2);
+ }
+ );
+
+ info(
+ "The text-area heights should increase when transitioning from vertical to horizontal orientation with content."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
+ { orientation: "horizontal" },
+ ],
+ [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ { textAreaHeights: "increased" },
+ ],
+ ],
+ },
+ async () => {
+ await ensureWindowSize(window, 1600 * Math.SQRT1_2, 900 * Math.SQRT1_2);
+ }
+ );
+
+ await cleanup();
+});
diff --git a/toolkit/components/translations/tests/browser/browser_about_translations_textarea_resize_by_zoom.js b/toolkit/components/translations/tests/browser/browser_about_translations_textarea_resize_by_zoom.js
@@ -0,0 +1,240 @@
+/* Any copyright is dedicated to the Public Domain.
+ https://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// The German lower-case character "ß" expands to two characters "SS" when capitalized.
+// Our mock translator deterministically capitalizes text for integration tests.
+const expandingInput = `\
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß \
+ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß ß`;
+
+/**
+ * This test case ensures that modifying the zoom level within the page's horizontal
+ * orientation does not cause the text areas to resize.
+ */
+add_task(
+ async function test_about_translations_horizontal_orientation_zoom_changes() {
+ const { aboutTranslationsTestUtils, cleanup } = await openAboutTranslations(
+ {
+ languagePairs: [
+ { fromLang: "de", toLang: "en" },
+ { fromLang: "en", toLang: "de" },
+ ],
+ autoDownloadFromRemoteSettings: true,
+ }
+ );
+
+ info(
+ "The text areas should expand when a large input is pasted as the source."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.TranslationRequested,
+ { translationId: 1 },
+ ],
+ [AboutTranslationsTestUtils.Events.ShowTranslatingPlaceholder],
+ [
+ AboutTranslationsTestUtils.Events.TranslationComplete,
+ { translationId: 1 },
+ ],
+ [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ {
+ textAreaHeights: "increased",
+ },
+ ],
+ ],
+ unexpected: [AboutTranslationsTestUtils.Events.PageOrientationChanged],
+ },
+ async () => {
+ await aboutTranslationsTestUtils.setSourceLanguageSelectorValue("de");
+ await aboutTranslationsTestUtils.setTargetLanguageSelectorValue("en");
+ await aboutTranslationsTestUtils.setSourceTextAreaValue(expandingInput);
+ }
+ );
+
+ await aboutTranslationsTestUtils.assertTranslatedText({
+ sourceLanguage: "de",
+ targetLanguage: "en",
+ sourceText: expandingInput,
+ });
+
+ info(
+ "The text area height should not change when the horizontal orientation is made wider, but remains horizontal."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ unexpected: [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
+ ],
+ },
+ () => {
+ FullZoom.setZoom(0.9 * Math.SQRT1_2);
+ }
+ );
+
+ info(
+ "The text area height should not change when the horizontal orientation is made narrower, but remains horizontal."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ unexpected: [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
+ ],
+ },
+ () => {
+ FullZoom.setZoom(1.1 * Math.SQRT1_2);
+ }
+ );
+
+ await cleanup();
+ }
+);
+
+/**
+ * This test case ensures that transitioning the page between its vertical and horizontal orientations via zoom
+ * may cause the text areas to resize, and that modifying the zoom level within the page's vertical orientation
+ * may cause the text areas to resize.
+ */
+add_task(
+ async function test_about_translations_vertical_orientation_zoom_changes() {
+ const { aboutTranslationsTestUtils, cleanup } = await openAboutTranslations(
+ {
+ languagePairs: [
+ { fromLang: "de", toLang: "en" },
+ { fromLang: "en", toLang: "de" },
+ ],
+ autoDownloadFromRemoteSettings: true,
+ }
+ );
+
+ info(
+ "The text-area heights should not change when transitioning to a vertical orientation with no content."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
+ { orientation: "vertical" },
+ ],
+ ],
+ unexpected: [AboutTranslationsTestUtils.Events.TextAreaHeightsChanged],
+ },
+ () => {
+ FullZoom.setZoom(1.5 * Math.SQRT1_2);
+ }
+ );
+
+ info(
+ "The text areas should expand when a large input is pasted as the source."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.TranslationRequested,
+ { translationId: 1 },
+ ],
+ [AboutTranslationsTestUtils.Events.ShowTranslatingPlaceholder],
+ [
+ AboutTranslationsTestUtils.Events.TranslationComplete,
+ { translationId: 1 },
+ ],
+ [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ {
+ textAreaHeights: "increased",
+ },
+ ],
+ ],
+ unexpected: [AboutTranslationsTestUtils.Events.PageOrientationChanged],
+ },
+ async () => {
+ await aboutTranslationsTestUtils.setSourceLanguageSelectorValue("de");
+ await aboutTranslationsTestUtils.setTargetLanguageSelectorValue("en");
+ await aboutTranslationsTestUtils.setSourceTextAreaValue(expandingInput);
+ }
+ );
+
+ await aboutTranslationsTestUtils.assertTranslatedText({
+ sourceLanguage: "de",
+ targetLanguage: "en",
+ sourceText: expandingInput,
+ });
+
+ info(
+ "The text-area heights should increase when making the vertical orientation narrower."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ { textAreaHeights: "increased" },
+ ],
+ ],
+ unexpected: [AboutTranslationsTestUtils.Events.PageOrientationChanged],
+ },
+ () => {
+ FullZoom.setZoom(4 * Math.SQRT1_2);
+ }
+ );
+
+ info(
+ "The text-area heights should decrease when making the vertical orientation wider."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ { textAreaHeights: "decreased" },
+ ],
+ ],
+ unexpected: [AboutTranslationsTestUtils.Events.PageOrientationChanged],
+ },
+ () => {
+ FullZoom.setZoom(1.5 * Math.SQRT1_2);
+ }
+ );
+
+ info(
+ "The text-area heights should increase when transitioning from vertical to horizontal orientation with content."
+ );
+ await aboutTranslationsTestUtils.assertEvents(
+ {
+ expected: [
+ [
+ AboutTranslationsTestUtils.Events.PageOrientationChanged,
+ { orientation: "horizontal" },
+ ],
+ [
+ AboutTranslationsTestUtils.Events.TextAreaHeightsChanged,
+ { textAreaHeights: "increased" },
+ ],
+ ],
+ },
+ () => {
+ FullZoom.setZoom(1.0 * Math.SQRT1_2);
+ }
+ );
+
+ await cleanup();
+ }
+);
diff --git a/toolkit/components/translations/tests/browser/shared-head.js b/toolkit/components/translations/tests/browser/shared-head.js
@@ -202,6 +202,11 @@ async function openAboutTranslations({
// Now load the about:translations page, since the actor could be mocked.
await loadNewPage(tab.linkedBrowser, "about:translations");
+ // Ensure the window always opens with a horizontal page layout.
+ // Divide everything by sqrt(2) to halve the overall content size.
+ await ensureWindowSize(window, 1600 * Math.SQRT1_2, 900 * Math.SQRT1_2);
+ FullZoom.setZoom(Math.SQRT1_2, tab.linkedBrowser);
+
/**
* @param {number} count - Count of the language pairs expected.
*/
@@ -2816,6 +2821,13 @@ class AboutTranslationsTestUtils {
static TranslationComplete = "AboutTranslations:TranslationComplete";
/**
+ * Event fired when the page layout changes.
+ *
+ * @type {string}
+ */
+ static PageOrientationChanged = "AboutTranslations:PageOrientationChanged";
+
+ /**
* Event fired when the source/target textarea heights change.
*
* @type {string}