From 36a699b418fd9d3351050f7a683b3ed9e0f653b3 Mon Sep 17 00:00:00 2001 From: Hanna Jones Date: Wed, 24 Apr 2024 19:16:46 +0000 Subject: [PATCH] Bug 1803678 - enable lazy loading of ESModule based moz- custom elements r=reusable-components-reviewers,pip-reviewers,credential-management-reviewers,translations-reviewers,kpatenio,issammani,mstriemer Differential Revision: https://phabricator.services.mozilla.com/D207445 --- browser/base/content/browser-addons.js | 5 -- browser/base/content/browser-places.js | 1 - browser/base/content/browser-siteIdentity.js | 1 - .../content/browser-sitePermissionPanel.js | 3 - .../base/content/browser-siteProtections.js | 3 - .../chrome/test_confirm_delete_dialog.html | 5 +- .../content/ContentAnalysis.sys.mjs | 1 - .../test/browser_PanelMultiView_keyboard.js | 1 - .../components/downloads/content/downloads.js | 2 - .../ExtensionControlledPopup.sys.mjs | 2 - .../screenshots/content/screenshots.js | 2 - .../docs/README.other-widgets.stories.md | 11 ++-- .../docs/README.reusable-widgets.stories.md | 9 ++- .../content/fullPageTranslationsPanel.js | 6 -- .../content/selectTranslationsPanel.js | 7 +-- browser/modules/ExtensionsUI.sys.mjs | 2 - dom/tests/mochitest/webcomponents/chrome.toml | 2 +- ...l => test_custom_element_auto_import.html} | 34 +++++------ .../default/FormAutofillPrompter.sys.mjs | 1 - .../pictureinpicture/PictureInPicture.sys.mjs | 2 - toolkit/content/customElements.js | 61 +++++++------------ toolkit/content/widgets/notificationbox.js | 3 +- toolkit/content/widgets/popupnotification.js | 2 - 23 files changed, 52 insertions(+), 114 deletions(-) rename dom/tests/mochitest/webcomponents/{test_custom_element_ensure_custom_element.html => test_custom_element_auto_import.html} (61%) diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js index 1180a1ff4969..e2360e3f3180 100644 --- a/browser/base/content/browser-addons.js +++ b/browser/base/content/browser-addons.js @@ -618,7 +618,6 @@ var gXPInstallObserver = { break; } case "addon-install-blocked": { - await window.ensureCustomElements("moz-support-link"); // Dismiss the progress notification. Note that this is bad if // there are multiple simultaneous installs happening, see // bug 1329884 for a longer explanation. @@ -1955,8 +1954,6 @@ var gUnifiedExtensions = { supportPage = null, type = "warning", }) { - window.ensureCustomElements("moz-message-bar"); - const messageBar = document.createElement("moz-message-bar"); messageBar.setAttribute("type", type); messageBar.classList.add("unified-extensions-message-bar"); @@ -1964,8 +1961,6 @@ var gUnifiedExtensions = { messageBar.setAttribute("data-l10n-attrs", "heading, message"); if (supportPage) { - window.ensureCustomElements("moz-support-link"); - const supportUrl = document.createElement("a", { is: "moz-support-link", }); diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js index 55d6b020825a..9ffd8c5c57e4 100644 --- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -55,7 +55,6 @@ var StarUI = { delete this.panel; this._createPanelIfNeeded(); var element = this._element("editBookmarkPanel"); - window.ensureCustomElements("moz-button-group"); // initially the panel is hidden // to avoid impacting startup / new window performance element.hidden = false; diff --git a/browser/base/content/browser-siteIdentity.js b/browser/base/content/browser-siteIdentity.js index 60bd4fc01cf1..eaed3950fead 100644 --- a/browser/base/content/browser-siteIdentity.js +++ b/browser/base/content/browser-siteIdentity.js @@ -177,7 +177,6 @@ var gIdentityHandler = { _popupInitialized: false, _initializePopup() { - window.ensureCustomElements("moz-support-link"); if (!this._popupInitialized) { let wrapper = document.getElementById("template-identity-popup"); wrapper.replaceWith(wrapper.content); diff --git a/browser/base/content/browser-sitePermissionPanel.js b/browser/base/content/browser-sitePermissionPanel.js index d977653f5134..de7b2cc39a43 100644 --- a/browser/base/content/browser-sitePermissionPanel.js +++ b/browser/base/content/browser-sitePermissionPanel.js @@ -14,9 +14,6 @@ var gPermissionPanel = { if (!this._popupInitialized) { let wrapper = document.getElementById("template-permission-popup"); wrapper.replaceWith(wrapper.content); - - window.ensureCustomElements("moz-support-link"); - this._popupInitialized = true; } }, diff --git a/browser/base/content/browser-siteProtections.js b/browser/base/content/browser-siteProtections.js index 043dc53b951b..5f649ab49ccd 100644 --- a/browser/base/content/browser-siteProtections.js +++ b/browser/base/content/browser-siteProtections.js @@ -1377,7 +1377,6 @@ var gProtectionsHandler = { let wrapper = document.getElementById("template-protections-popup"); this._protectionsPopup = wrapper.content.firstElementChild; wrapper.replaceWith(wrapper.content); - window.ensureCustomElements("moz-support-link"); this.maybeSetMilestoneCounterText(); @@ -1591,8 +1590,6 @@ var gProtectionsHandler = { // Add an observer to observe that the history has been cleared. Services.obs.addObserver(this, "browser:purge-session-history"); - - window.ensureCustomElements("moz-button-group", "moz-toggle"); }, uninit() { diff --git a/browser/components/aboutlogins/tests/chrome/test_confirm_delete_dialog.html b/browser/components/aboutlogins/tests/chrome/test_confirm_delete_dialog.html index 68a58aee4fd7..afbae0c310fa 100644 --- a/browser/components/aboutlogins/tests/chrome/test_confirm_delete_dialog.html +++ b/browser/components/aboutlogins/tests/chrome/test_confirm_delete_dialog.html @@ -24,6 +24,7 @@ Test the confirmation-dialog component ``` - -Or use `window.ensureCustomElements("")` as previously stated in [the using new design system components section.](#using-new-design-system-components) diff --git a/browser/components/storybook/docs/README.reusable-widgets.stories.md b/browser/components/storybook/docs/README.reusable-widgets.stories.md index f26c18a2b064..aaa131223964 100644 --- a/browser/components/storybook/docs/README.reusable-widgets.stories.md +++ b/browser/components/storybook/docs/README.reusable-widgets.stories.md @@ -111,17 +111,16 @@ by updating [this array](https://searchfox.org/mozilla-central/rev/5c922d8b93b43 Once you've added a new component to `toolkit/content/widgets` and created `chrome://` URLs via `toolkit/content/jar.mn` you should be able to start using it -throughout Firefox. You can import the component into `html`/`xhtml` files via a +throughout Firefox. In most cases, you should be able to rely on your custom element getting lazy loaded at the time of first use, provided you are working in a privileged context where `customElements.js` is available. + +You can import the component directly into `html`/`xhtml` files via a `script` tag with `type="module"`: ```html ``` -If you are unable to import the new component in html, you can use [`ensureCustomElements()` in customElements.js](https://searchfox.org/mozilla-central/rev/31f5847a4494b3646edabbdd7ea39cb88509afe2/toolkit/content/customElements.js#865) in the relevant JS file. -For example, [we use `window.ensureCustomElements("moz-button-group")` in `browser-siteProtections.js`](https://searchfox.org/mozilla-central/rev/31f5847a4494b3646edabbdd7ea39cb88509afe2/browser/base/content/browser-siteProtections.js#1749). -**Note** you will need to add your new widget to [the switch in importCustomElementFromESModule](https://searchfox.org/mozilla-central/rev/85b4f7363292b272eb9b606e00de2c37a6be73f0/toolkit/content/customElements.js#845-859) for `ensureCustomElements()` to work as expected. -Once [Bug 1803810](https://bugzilla.mozilla.org/show_bug.cgi?id=1803810) lands, this process will be simplified: you won't need to use `ensureCustomElements()` and you will [add your widget to the appropriate array in customElements.js instead of the switch statement](https://searchfox.org/mozilla-central/rev/85b4f7363292b272eb9b606e00de2c37a6be73f0/toolkit/content/customElements.js#818-841). +**Note** you will need to add your new widget to [this array in customElements.js](https://searchfox.org/mozilla-central/rev/cde3d4a8d228491e8b7f1bd94c63bbe039850696/toolkit/content/customElements.js#791-810) to ensure it gets lazy loaded on creation. ## Common pitfalls diff --git a/browser/components/translations/content/fullPageTranslationsPanel.js b/browser/components/translations/content/fullPageTranslationsPanel.js index af537821900b..03a1972b80fa 100644 --- a/browser/components/translations/content/fullPageTranslationsPanel.js +++ b/browser/components/translations/content/fullPageTranslationsPanel.js @@ -1043,8 +1043,6 @@ var FullPageTranslationsPanel = new (class { isFirstUserInteraction = null, } ) { - await window.ensureCustomElements("moz-button-group"); - const { panel, appMenuButton } = this.elements; const openedFromAppMenu = target.id === appMenuButton.id; const { docLangTag } = await this.#getCachedDetectedLanguages(); @@ -1109,10 +1107,6 @@ var FullPageTranslationsPanel = new (class { return; } - const window = - gBrowser.selectedBrowser.browsingContext.top.embedderElement.ownerGlobal; - window.ensureCustomElements("moz-support-link"); - const { button } = this.buttonElements; const { requestedTranslationPair } = diff --git a/browser/components/translations/content/selectTranslationsPanel.js b/browser/components/translations/content/selectTranslationsPanel.js index 5f40fba8f603..5673e039176e 100644 --- a/browser/components/translations/content/selectTranslationsPanel.js +++ b/browser/components/translations/content/selectTranslationsPanel.js @@ -393,7 +393,7 @@ var SelectTranslationsPanel = new (class { ); } - await this.#openPopup(event, screenX, screenY); + this.#openPopup(event, screenX, screenY); } /** @@ -403,10 +403,7 @@ var SelectTranslationsPanel = new (class { * @param {number} screenX - The x-axis location of the screen at which to open the popup. * @param {number} screenY - The y-axis location of the screen at which to open the popup. */ - async #openPopup(event, screenX, screenY) { - await window.ensureCustomElements("moz-button-group"); - await window.ensureCustomElements("moz-message-bar"); - + #openPopup(event, screenX, screenY) { this.console?.log("Showing SelectTranslationsPanel"); const { panel } = this.elements; panel.openPopupAtScreen(screenX, screenY, /* isContextMenu */ false, event); diff --git a/browser/modules/ExtensionsUI.sys.mjs b/browser/modules/ExtensionsUI.sys.mjs index 961cafad4851..8669dea80125 100644 --- a/browser/modules/ExtensionsUI.sys.mjs +++ b/browser/modules/ExtensionsUI.sys.mjs @@ -339,8 +339,6 @@ export var ExtensionsUI = { async showPermissionsPrompt(target, strings, icon) { let { browser, window } = getTabBrowser(target); - await window.ensureCustomElements("moz-support-link"); - // Wait for any pending prompts to complete before showing the next one. let pending; while ((pending = this.pendingNotifications.get(browser))) { diff --git a/dom/tests/mochitest/webcomponents/chrome.toml b/dom/tests/mochitest/webcomponents/chrome.toml index b29df619c419..5db7f14a37c7 100644 --- a/dom/tests/mochitest/webcomponents/chrome.toml +++ b/dom/tests/mochitest/webcomponents/chrome.toml @@ -1,7 +1,7 @@ [DEFAULT] support-files = ["dummy_page.html"] -["test_custom_element_ensure_custom_element.html"] +["test_custom_element_auto_import.html"] ["test_custom_element_htmlconstructor_chrome.html"] support-files = [ diff --git a/dom/tests/mochitest/webcomponents/test_custom_element_ensure_custom_element.html b/dom/tests/mochitest/webcomponents/test_custom_element_auto_import.html similarity index 61% rename from dom/tests/mochitest/webcomponents/test_custom_element_ensure_custom_element.html rename to dom/tests/mochitest/webcomponents/test_custom_element_auto_import.html index 03ed2e381560..23716006779b 100644 --- a/dom/tests/mochitest/webcomponents/test_custom_element_ensure_custom_element.html +++ b/dom/tests/mochitest/webcomponents/test_custom_element_auto_import.html @@ -1,11 +1,8 @@ - - Test for customElements.ensureCustomElement + Test for custom element auto import behavior diff --git a/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs b/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs index 93fe3307420c..aada13bbb753 100644 --- a/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs +++ b/toolkit/components/formautofill/default/FormAutofillPrompter.sys.mjs @@ -1358,7 +1358,6 @@ export let FormAutofillPrompter = { ); const { ownerGlobal: win } = browser; - await win.ensureCustomElements("moz-support-link"); win.MozXULElement.insertFTLIfNeeded( "toolkit/formautofill/formAutofill.ftl" ); diff --git a/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs b/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs index 2356548bf8de..6265b83b3cf5 100644 --- a/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs +++ b/toolkit/components/pictureinpicture/PictureInPicture.sys.mjs @@ -246,8 +246,6 @@ export var PictureInPicture = { let panel = browser.ownerDocument.querySelector("#PictureInPicturePanel"); if (!panel) { - browser.ownerGlobal.ensureCustomElements("moz-toggle"); - browser.ownerGlobal.ensureCustomElements("moz-support-link"); let template = browser.ownerDocument.querySelector( "#PictureInPicturePanelTemplate" ); diff --git a/toolkit/content/customElements.js b/toolkit/content/customElements.js index ef58963a027c..f2847e48d020 100644 --- a/toolkit/content/customElements.js +++ b/toolkit/content/customElements.js @@ -806,49 +806,32 @@ "chrome://global/content/elements/autocomplete-input.js", ], ["editor", "chrome://global/content/elements/editor.js"], + ["moz-button", "chrome://global/content/elements/moz-button.mjs"], + [ + "moz-button-group", + "chrome://global/content/elements/moz-button-group.mjs", + ], + ["moz-card", "chrome://global/content/elements/moz-card.mjs"], + ["moz-five-star", "chrome://global/content/elements/moz-five-star.mjs"], + [ + "moz-message-bar", + "chrome://global/content/elements/moz-message-bar.mjs", + ], + ["moz-page-nav", "chrome://global/content/elements/moz-page-nav.mjs"], + [ + "moz-support-link", + "chrome://global/content/elements/moz-support-link.mjs", + ], + ["moz-toggle", "chrome://global/content/elements/moz-toggle.mjs"], ]) { customElements.setElementCreationCallback(tag, () => { - Services.scriptloader.loadSubScript(script, window); + if (script.endsWith(".mjs")) { + ChromeUtils.importESModule(script, { global: "current" }); + } else { + Services.scriptloader.loadSubScript(script, window); + } }); } - // Bug 1813077: This is a workaround until Bug 1803810 lands - // which will give us the ability to load ESMs synchronously - // like the previous Services.scriptloader.loadSubscript() function - function importCustomElementFromESModule(name) { - switch (name) { - case "moz-button": - return import("chrome://global/content/elements/moz-button.mjs"); - case "moz-button-group": - return import( - "chrome://global/content/elements/moz-button-group.mjs" - ); - case "moz-message-bar": - return import("chrome://global/content/elements/moz-message-bar.mjs"); - case "moz-support-link": - return import( - "chrome://global/content/elements/moz-support-link.mjs" - ); - case "moz-toggle": - return import("chrome://global/content/elements/moz-toggle.mjs"); - case "moz-card": - return import("chrome://global/content/elements/moz-card.mjs"); - } - throw new Error(`Unknown custom element name (${name})`); - } - - /* - This function explicitly returns null so that there is no confusion - about which custom elements from ES Modules have been loaded. - */ - window.ensureCustomElements = function (...elementNames) { - return Promise.all( - elementNames - .filter(name => !customElements.get(name)) - .map(name => importCustomElementFromESModule(name)) - ) - .then(() => null) - .catch(console.error); - }; // Immediately load the following elements for (let script of [ diff --git a/toolkit/content/widgets/notificationbox.js b/toolkit/content/widgets/notificationbox.js index 8cfc7b865cb8..fc3e553ca424 100644 --- a/toolkit/content/widgets/notificationbox.js +++ b/toolkit/content/widgets/notificationbox.js @@ -621,7 +621,7 @@ customElements.define("notification", MozElements.Notification); async function createNotificationMessageElement() { - await window.ensureCustomElements("moz-message-bar"); + document.createElement("moz-message-bar"); let MozMessageBar = await customElements.whenDefined("moz-message-bar"); class NotificationMessage extends MozMessageBar { static queries = { @@ -772,7 +772,6 @@ let buttonElem; if (button.hasOwnProperty("supportPage")) { - window.ensureCustomElements("moz-support-link"); buttonElem = document.createElement("a", { is: "moz-support-link", }); diff --git a/toolkit/content/widgets/popupnotification.js b/toolkit/content/widgets/popupnotification.js index 835151496c5c..7dfa47728b36 100644 --- a/toolkit/content/widgets/popupnotification.js +++ b/toolkit/content/widgets/popupnotification.js @@ -116,8 +116,6 @@ MozXULElement.insertFTLIfNeeded("toolkit/global/popupnotification.ftl"); this.appendChild(this.constructor.fragment); - window.ensureCustomElements("moz-button-group"); - this.button = this.querySelector(".popup-notification-primary-button"); if ( this.hasAttribute("buttonlabel") ||