tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mtoolkit/components/translations/content/about-translations.mjs | 138+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mtoolkit/components/translations/tests/browser/browser.toml | 4++++
Mtoolkit/components/translations/tests/browser/browser_about_translations_textarea_resize_by_input.js | 9++++++++-
Atoolkit/components/translations/tests/browser/browser_about_translations_textarea_resize_by_window.js | 236+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atoolkit/components/translations/tests/browser/browser_about_translations_textarea_resize_by_zoom.js | 240+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/components/translations/tests/browser/shared-head.js | 12++++++++++++
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}