summaryrefslogtreecommitdiffstats
path: root/browser/components/translations/content/TranslationsPanelShared.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--browser/components/translations/content/TranslationsPanelShared.sys.mjs93
1 files changed, 81 insertions, 12 deletions
diff --git a/browser/components/translations/content/TranslationsPanelShared.sys.mjs b/browser/components/translations/content/TranslationsPanelShared.sys.mjs
index 570528df3f..f5045f57e0 100644
--- a/browser/components/translations/content/TranslationsPanelShared.sys.mjs
+++ b/browser/components/translations/content/TranslationsPanelShared.sys.mjs
@@ -11,9 +11,53 @@ ChromeUtils.defineESModuleGetters(lazy, {
/**
* A class containing static functionality that is shared by both
* the FullPageTranslationsPanel and SelectTranslationsPanel classes.
+ *
+ * It is recommended to read the documentation above the TranslationsParent class
+ * definition to understand the scope of the Translations architecture throughout
+ * Firefox.
+ *
+ * @see TranslationsParent
+ *
+ * The static instance of this class is a singleton in the parent process, and is
+ * available throughout all windows and tabs, just like the static instance of
+ * the TranslationsParent class.
+ *
+ * Unlike the TranslationsParent, this class is never instantiated as an actor
+ * outside of the static-context functionality defined below.
*/
export class TranslationsPanelShared {
- static #langListsInitState = new Map();
+ /**
+ * A map from Translations Panel instances to their initialized states.
+ * There is one instance of each panel per top ChromeWindow in Firefox.
+ *
+ * See the documentation above the TranslationsParent class for a detailed
+ * explanation of the translations architecture throughout Firefox.
+ *
+ * @see TranslationsParent
+ *
+ * @type {Map<FullPageTranslationsPanel | SelectTranslationsPanel, string>}
+ */
+ static #langListsInitState = new WeakMap();
+
+ /**
+ * True if the next language-list initialization to fail for testing.
+ *
+ * @see TranslationsPanelShared.ensureLangListsBuilt
+ *
+ * @type {boolean}
+ */
+ static #simulateLangListError = false;
+
+ /**
+ * Clears cached data regarding the initialization state of the
+ * FullPageTranslationsPanel or the SelectTranslationsPanel.
+ *
+ * This is only needed for test runners to ensure that each test
+ * starts from a clean slate.
+ */
+ static clearCache() {
+ this.#langListsInitState = new WeakMap();
+ }
/**
* Defines lazy getters for accessing elements in the document based on provided entries.
@@ -46,13 +90,25 @@ export class TranslationsPanelShared {
}
/**
+ * Ensures that the next call to ensureLangListBuilt wil fail
+ * for the purpose of testing the error state.
+ *
+ * @see TranslationsPanelShared.ensureLangListsBuilt
+ *
+ * @type {boolean}
+ */
+ static simulateLangListError() {
+ this.#simulateLangListError = true;
+ }
+
+ /**
* Retrieves the initialization state of language lists for the specified panel.
*
* @param {FullPageTranslationsPanel | SelectTranslationsPanel} panel
* - The panel for which to look up the state.
*/
static getLangListsInitState(panel) {
- return TranslationsPanelShared.#langListsInitState.get(panel.id);
+ return TranslationsPanelShared.#langListsInitState.get(panel);
}
/**
@@ -64,17 +120,17 @@ export class TranslationsPanelShared {
* @param {FullPageTranslationsPanel | SelectTranslationsPanel} panel
* - The panel for which to ensure language lists are built.
*/
- static async ensureLangListsBuilt(document, panel, innerWindowId) {
- const { id } = panel;
- switch (
- TranslationsPanelShared.#langListsInitState.get(`${id}-${innerWindowId}`)
- ) {
+ static async ensureLangListsBuilt(document, panel) {
+ const { panel: panelElement } = panel.elements;
+ switch (TranslationsPanelShared.#langListsInitState.get(panel)) {
case "initialized":
// This has already been initialized.
return;
case "error":
case undefined:
- // attempt to initialize
+ // Set the error state in case there is an early exit at any point.
+ // This will be set to "initialized" if everything succeeds.
+ TranslationsPanelShared.#langListsInitState.set(panel, "error");
break;
default:
throw new Error(
@@ -88,18 +144,28 @@ export class TranslationsPanelShared {
await lazy.TranslationsParent.getSupportedLanguages();
// Verify that we are in a proper state.
- if (languagePairs.length === 0) {
+ if (languagePairs.length === 0 || this.#simulateLangListError) {
+ this.#simulateLangListError = false;
throw new Error("No translation languages were retrieved.");
}
- const fromPopups = panel.querySelectorAll(
+ const fromPopups = panelElement.querySelectorAll(
".translations-panel-language-menupopup-from"
);
- const toPopups = panel.querySelectorAll(
+ const toPopups = panelElement.querySelectorAll(
".translations-panel-language-menupopup-to"
);
for (const popup of fromPopups) {
+ // For the moment, the FullPageTranslationsPanel includes its own
+ // menu item for "Choose another language" as the first item in the list
+ // with an empty-string for its value. The SelectTranslationsPanel has
+ // only languages in its list with BCP-47 tags for values. As such,
+ // this loop works for both panels, to remove all of the languages
+ // from the list, but ensuring that any empty-string items are retained.
+ while (popup.lastChild?.value) {
+ popup.lastChild.remove();
+ }
for (const { langTag, displayName } of fromLanguages) {
const fromMenuItem = document.createXULElement("menuitem");
fromMenuItem.setAttribute("value", langTag);
@@ -109,6 +175,9 @@ export class TranslationsPanelShared {
}
for (const popup of toPopups) {
+ while (popup.lastChild?.value) {
+ popup.lastChild.remove();
+ }
for (const { langTag, displayName } of toLanguages) {
const toMenuItem = document.createXULElement("menuitem");
toMenuItem.setAttribute("value", langTag);
@@ -117,6 +186,6 @@ export class TranslationsPanelShared {
}
}
- TranslationsPanelShared.#langListsInitState.set(id, "initialized");
+ TranslationsPanelShared.#langListsInitState.set(panel, "initialized");
}
}