diff --git a/browser/actors/PluginParent.sys.mjs b/browser/actors/PluginParent.sys.mjs index ba4b82fa3455..fa93c1d5ab90 100644 --- a/browser/actors/PluginParent.sys.mjs +++ b/browser/actors/PluginParent.sys.mjs @@ -178,11 +178,20 @@ export class PluginParent extends JSWindowActorParent { buttons.push(submitButton); } + // Add the "learn more" link. + let learnMoreLink = { + supportPage: "plugin-crashed-notificationbar", + label: lazy.gNavigatorBundle.GetStringFromName( + "crashedpluginsMessage.learnMore" + ), + }; + buttons.push(learnMoreLink); + let messageString = lazy.gNavigatorBundle.formatStringFromName( "crashedpluginsMessage.title", [report.pluginName] ); - notification = notificationBox.appendNotification( + notificationBox.appendNotification( "plugin-crashed", { label: messageString, @@ -191,21 +200,5 @@ export class PluginParent extends JSWindowActorParent { }, buttons ); - - // Add the "learn more" link. - let link = notification.ownerDocument.createXULElement("label", { - is: "text-link", - }); - link.setAttribute( - "value", - lazy.gNavigatorBundle.GetStringFromName("crashedpluginsMessage.learnMore") - ); - let crashurl = Services.urlFormatter.formatURLPref("app.support.baseURL"); - crashurl += "plugin-crashed-notificationbar"; - link.href = crashurl; - // Append a blank text node to make sure we don't put - // the link right next to the end of the message text. - notification.messageText.appendChild(new Text(" ")); - notification.messageText.appendChild(link); } } diff --git a/browser/base/content/browser-data-submission-info-bar.js b/browser/base/content/browser-data-submission-info-bar.js index 896542796c54..26d9affb2920 100644 --- a/browser/base/content/browser-data-submission-info-bar.js +++ b/browser/base/content/browser-data-submission-info-bar.js @@ -40,7 +40,7 @@ var gDataNotificationInfoBar = { return gNotificationBox.getNotificationWithValue(name); }, - _displayDataPolicyInfoBar(request) { + async _displayDataPolicyInfoBar(request) { if (this._getDataReportingNotification()) { return; } @@ -59,7 +59,7 @@ var gDataNotificationInfoBar = { ]; this._log.info("Creating data reporting policy notification."); - gNotificationBox.appendNotification( + await gNotificationBox.appendNotification( this._DATA_REPORTING_NOTIFICATION, { label: { diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index b899b27878f8..4197d33be2d3 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1106,7 +1106,7 @@ const gStoragePressureObserver = { } messageFragment.appendChild(message); - gNotificationBox.appendNotification( + await gNotificationBox.appendNotification( NOTIFICATION_VALUE, { label: messageFragment, @@ -1122,7 +1122,7 @@ const gStoragePressureObserver = { }; var gPopupBlockerObserver = { - handleEvent(aEvent) { + async handleEvent(aEvent) { if (aEvent.originalTarget != gBrowser.selectedBrowser) { return; } @@ -1158,23 +1158,31 @@ var gPopupBlockerObserver = { let notificationBox = gBrowser.getNotificationBox(); let notification = - notificationBox.getNotificationWithValue("popup-blocked"); + notificationBox.getNotificationWithValue("popup-blocked") || + (await this.notificationPromise); if (notification) { notification.label = label; } else { const image = "chrome://browser/skin/notification-icons/popup.svg"; const priority = notificationBox.PRIORITY_INFO_MEDIUM; - notificationBox.appendNotification( - "popup-blocked", - { label, image, priority }, - [ - { - "l10n-id": "popup-warning-button", - popup: "blockedPopupOptions", - callback: null, - }, - ] - ); + try { + this.notificationPromise = notificationBox.appendNotification( + "popup-blocked", + { label, image, priority }, + [ + { + "l10n-id": "popup-warning-button", + popup: "blockedPopupOptions", + callback: null, + }, + ] + ); + await this.notificationPromise; + } catch (err) { + console.warn(err); + } finally { + this.notificationPromise = null; + } } } @@ -1402,7 +1410,7 @@ var gKeywordURIFixup = { let asciiHost = fixedURI.asciiHost; let onLookupCompleteListener = { - onLookupComplete(request, record, status) { + async onLookupComplete(request, record, status) { let browserRef = weakBrowser.get(); if (!Components.isSuccessCode(status) || !browserRef) { return; @@ -1457,7 +1465,7 @@ var gKeywordURIFixup = { }, }, ]; - let notification = notificationBox.appendNotification( + let notification = await notificationBox.appendNotification( "keyword-uri-fixup", { label: message, @@ -3334,22 +3342,9 @@ function PageProxyClickHandler(aEvent) { * us via async messaging. */ var BrowserOnClick = { - ignoreWarningLink(reason, blockedInfo, browsingContext) { - let triggeringPrincipal = - blockedInfo.triggeringPrincipal || - _createNullPrincipalFromTabUserContextId(); - - // Allow users to override and continue through to the site, - // but add a notify bar as a reminder, so that they don't lose - // track after, e.g., tab switching. - // Note that we have to use the passed URI info and can't just - // rely on the document URI, because the latter contains - // additional query parameters that should be stripped. - browsingContext.fixupAndLoadURIString(blockedInfo.uri, { - triggeringPrincipal, - flags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, - }); - + async ignoreWarningLink(reason, blockedInfo, browsingContext) { + // Add a notify bar before allowing the user to continue through to the + // site, so that they don't lose track after, e.g., tab switching. // We can't use browser.contentPrincipal which is principal of about:blocked // Create one from uri with current principal origin attributes let principal = Services.scriptSecurityManager.createContentPrincipal( @@ -3424,7 +3419,20 @@ var BrowserOnClick = { // provide a URL endpoint for these reports. } - SafeBrowsingNotificationBox.show(title, buttons); + await SafeBrowsingNotificationBox.show(title, buttons); + + // Allow users to override and continue through to the site. + // Note that we have to use the passed URI info and can't just + // rely on the document URI, because the latter contains + // additional query parameters that should be stripped. + let triggeringPrincipal = + blockedInfo.triggeringPrincipal || + _createNullPrincipalFromTabUserContextId(); + + browsingContext.fixupAndLoadURIString(blockedInfo.uri, { + triggeringPrincipal, + flags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, + }); }, }; @@ -9059,7 +9067,7 @@ var PanicButtonNotifier = { const SafeBrowsingNotificationBox = { _currentURIBaseDomain: null, - show(title, buttons) { + async show(title, buttons) { let uri = gBrowser.currentURI; // start tracking host so that we know when we leave the domain @@ -9080,7 +9088,7 @@ const SafeBrowsingNotificationBox = { notificationBox.removeNotification(previousNotification); } - let notification = notificationBox.appendNotification( + let notification = await notificationBox.appendNotification( value, { label: title, diff --git a/browser/base/content/test/general/browser_datachoices_notification.js b/browser/base/content/test/general/browser_datachoices_notification.js index fd57140b6851..5a13feedd177 100644 --- a/browser/base/content/test/general/browser_datachoices_notification.js +++ b/browser/base/content/test/general/browser_datachoices_notification.js @@ -176,6 +176,7 @@ add_task(async function test_single_window() { // Wait for the infobar to be displayed. triggerInfoBar(10 * 1000); await alertShownPromise; + await promiseNextTick(); Assert.equal( gNotificationBox.allNotifications.length, diff --git a/browser/base/content/test/general/browser_refreshBlocker.js b/browser/base/content/test/general/browser_refreshBlocker.js index cbeb9efdbcd1..637f47a545e8 100644 --- a/browser/base/content/test/general/browser_refreshBlocker.js +++ b/browser/base/content/test/general/browser_refreshBlocker.js @@ -187,21 +187,23 @@ add_task(async function test_can_update_notification() { // should have fired, so the notification should be visible. let notificationBox = gBrowser.getNotificationBox(browser); let notification = notificationBox.currentNotification; + let [redirectLabel, refreshLabel] = await document.l10n.formatValues([ + { id: "refresh-blocked-redirect-label" }, + { id: "refresh-blocked-refresh-label" }, + ]); - let message = notification.messageText.querySelector("span"); is( - message.dataset.l10nId, - "refresh-blocked-redirect-label", + notification.messageText.textContent.trim(), + redirectLabel, "Should be showing the redirect message" ); // Next, attempt a refresh await attemptFakeRefresh(browser, false); - message = notification.messageText.querySelector("span"); is( - message.dataset.l10nId, - "refresh-blocked-refresh-label", + notification.messageText.textContent.trim(), + refreshLabel, "Should be showing the refresh message" ); } diff --git a/browser/base/content/test/notificationbox/browser_notificationbar_telemetry.js b/browser/base/content/test/notificationbox/browser_notificationbar_telemetry.js index 7810d4022d70..c775f2f9e983 100644 --- a/browser/base/content/test/notificationbox/browser_notificationbar_telemetry.js +++ b/browser/base/content/test/notificationbox/browser_notificationbar_telemetry.js @@ -31,15 +31,16 @@ add_task(async function showNotification() { verifyTelemetry("initial", 0, 0, 0, 0, 0, 0); - let notif3 = box3.appendNotification("infobar-testtwo-value", { + let notif3 = await box3.appendNotification("infobar-testtwo-value", { label: "Message for tab 3", priority: box3.PRIORITY_INFO_HIGH, telemetry: TELEMETRY_BASE + "testtwo", }); + await notif3.updateComplete; verifyTelemetry("first notification", 0, 0, 0, 0, 0, 1); - let notif1 = box1.appendNotification( + let notif1 = await box1.appendNotification( "infobar-testone-value", { label: "Message for tab 1", @@ -60,6 +61,7 @@ add_task(async function showNotification() { }, ] ); + await notif1.updateComplete; verifyTelemetry("second notification", 0, 0, 0, 0, 0, 1); await BrowserTestUtils.switchTab(gBrowser, tab1); @@ -89,7 +91,7 @@ add_task(async function showNotification() { notif3.dismiss(); verifyTelemetry("dismiss notification for box 3", 1, 1, 1, 1, 1, 1, 1); - let notif4 = box1.appendNotification( + let notif4 = await box1.appendNotification( "infobar-testtwo-value", { label: "Additional message for tab 1", @@ -103,13 +105,14 @@ add_task(async function showNotification() { }, ] ); + await notif4.updateComplete; verifyTelemetry("show first filtered notification", 2, 1, 1, 1, 1, 1, 1); notif4.buttonContainer.lastElementChild.click(); notif4.dismiss(); verifyTelemetry("dismiss first filtered notification", 2, 1, 1, 1, 1, 1, 1); - let notif5 = box1.appendNotification( + let notif5 = await box1.appendNotification( "infobar-testtwo-value", { label: "Dimissed additional message for tab 1", @@ -123,13 +126,14 @@ add_task(async function showNotification() { }, ] ); + await notif5.updateComplete; verifyTelemetry("show second filtered notification", 2, 1, 1, 1, 1, 1, 1); notif5.buttonContainer.lastElementChild.click(); notif5.dismiss(); verifyTelemetry("dismiss second filtered notification", 2, 1, 1, 1, 2, 1, 1); - let notif6 = box1.appendNotification( + let notif6 = await box1.appendNotification( "infobar-testtwo-value", { label: "Dimissed additional message for tab 1", @@ -144,6 +148,7 @@ add_task(async function showNotification() { }, ] ); + await notif6.updateComplete; verifyTelemetry("show third filtered notification", 2, 1, 1, 1, 2, 1, 1); notif6.buttonContainer.lastElementChild.click(); diff --git a/browser/base/content/test/notificationbox/browser_tabnotificationbox_switch_tabs.js b/browser/base/content/test/notificationbox/browser_tabnotificationbox_switch_tabs.js index f00916c77357..230336606b7d 100644 --- a/browser/base/content/test/notificationbox/browser_tabnotificationbox_switch_tabs.js +++ b/browser/base/content/test/notificationbox/browser_tabnotificationbox_switch_tabs.js @@ -32,9 +32,9 @@ function assertNotificationBoxShown(reason, browser) { is(selectedViewName, name, `Box is shown ${reason}`); } -function createNotification({ browser, label, value, priority }) { +async function createNotification({ browser, label, value, priority }) { let notificationBox = gBrowser.getNotificationBox(browser); - let notification = notificationBox.appendNotification(value, { + let notification = await notificationBox.appendNotification(value, { label, priority: notificationBox[priority], }); @@ -52,7 +52,7 @@ add_task(async function testNotificationInBackgroundTab() { gBrowser.selectedTab = firstTab; assertNotificationBoxHidden("initial first tab"); - createNotification({ + await createNotification({ browser, label: "My notification body", value: "test-notification", @@ -69,7 +69,7 @@ add_task(async function testNotificationInActiveTab() { await BrowserTestUtils.withNewTab("about:blank", async browser => { ok(!gBrowser.readNotificationBox(browser), "No notifications for new tab"); - createNotification({ + await createNotification({ browser, label: "Notification!", value: "test-notification", @@ -108,7 +108,7 @@ add_task(async function testNotificationMultipleTabs() { assertNotificationBoxHidden("after open", browserTwo); assertNotificationBoxHidden("after open", browserThree); - createNotification({ + await createNotification({ browser: browserTwo, label: "Test blank", value: "blank", @@ -121,7 +121,7 @@ add_task(async function testNotificationMultipleTabs() { assertNotificationBoxHidden("hidden create", browserTwo); assertNotificationBoxHidden("other create", browserThree); - createNotification({ + await createNotification({ browser: browserThree, label: "Test active tab", value: "active", diff --git a/browser/base/content/test/plugins/browser_enable_DRM_prompt.js b/browser/base/content/test/plugins/browser_enable_DRM_prompt.js index e77455ddd0d7..bc861806b194 100644 --- a/browser/base/content/test/plugins/browser_enable_DRM_prompt.js +++ b/browser/base/content/test/plugins/browser_enable_DRM_prompt.js @@ -36,6 +36,11 @@ add_task(async function test_drm_prompt_shows_for_toplevel() { // Turn off EME and Widevine CDM. Services.prefs.setBoolPref("media.eme.enabled", false); Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", false); + let notificationShownPromise = BrowserTestUtils.waitForNotificationBar( + gBrowser, + browser, + "drmContentDisabled" + ); // Have content request access to Widevine, UI should drop down to // prompt user to enable DRM. @@ -64,7 +69,9 @@ add_task(async function test_drm_prompt_shows_for_toplevel() { // Verify the UI prompt showed. let box = gBrowser.getNotificationBox(browser); + await notificationShownPromise; let notification = box.currentNotification; + await notification.updateComplete; ok(notification, "Notification should be visible"); is( @@ -152,6 +159,11 @@ add_task(async function test_drm_prompt_shows_for_cross_origin_iframe() { // Turn off EME and Widevine CDM. Services.prefs.setBoolPref("media.eme.enabled", false); Services.prefs.setBoolPref("media.gmp-widevinecdm.enabled", false); + let notificationShownPromise = BrowserTestUtils.waitForNotificationBar( + gBrowser, + browser, + "drmContentDisabled" + ); // Have content request access to Widevine, UI should drop down to // prompt user to enable DRM. @@ -198,7 +210,9 @@ add_task(async function test_drm_prompt_shows_for_cross_origin_iframe() { // Verify the UI prompt showed. let box = gBrowser.getNotificationBox(browser); + await notificationShownPromise; let notification = box.currentNotification; + await notification.updateComplete; ok(notification, "Notification should be visible"); is( diff --git a/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js b/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js index 483c2b403262..38ac3864c024 100644 --- a/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js +++ b/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js @@ -54,7 +54,7 @@ add_task(async function () { "Correct priority." ); is( - notification.messageText.textContent, + notification.messageText.textContent.trim(), "The GlobalTestPlugin plugin has crashed.", "Correct message." ); diff --git a/browser/components/BrowserGlue.sys.mjs b/browser/components/BrowserGlue.sys.mjs index 4290ec7f1cef..b74ee0dc91ac 100644 --- a/browser/components/BrowserGlue.sys.mjs +++ b/browser/components/BrowserGlue.sys.mjs @@ -3736,12 +3736,12 @@ BrowserGlue.prototype = { * Show the notificationBox for a locked places database. */ _showPlacesLockedNotificationBox: - function BG__showPlacesLockedNotificationBox() { + async function BG__showPlacesLockedNotificationBox() { var win = lazy.BrowserWindowTracker.getTopWindow(); var buttons = [{ supportPage: "places-locked" }]; var notifyBox = win.gBrowser.getNotificationBox(); - var notification = notifyBox.appendNotification( + var notification = await notifyBox.appendNotification( "places-locked", { label: { "l10n-id": "places-locked-prompt" }, @@ -4616,7 +4616,7 @@ BrowserGlue.prototype = { ]; const notifyBox = win.gBrowser.getNotificationBox(); - const notification = notifyBox.appendNotification( + const notification = await notifyBox.appendNotification( "startup-restore-session-suggestion", { label: messageFragment, diff --git a/browser/components/protocolhandler/test/browser/browser_registerProtocolHandler_notification.js b/browser/components/protocolhandler/test/browser/browser_registerProtocolHandler_notification.js index e96ed7060eea..e2598ae2817f 100644 --- a/browser/components/protocolhandler/test/browser/browser_registerProtocolHandler_notification.js +++ b/browser/components/protocolhandler/test/browser/browser_registerProtocolHandler_notification.js @@ -40,16 +40,9 @@ add_task(async function () { "info", "We expect this notification to have the type of 'info'." ); - - // Make sure the CSS is fully loaded... - ok( - await TestUtils.waitForCondition( - () => - notification.ownerGlobal.getComputedStyle( - notification.messageImage, - "::after" - ).backgroundImage == 'url("chrome://global/skin/icons/info-filled.svg")' - ), + is( + notification.messageImage.getAttribute("src"), + "chrome://global/skin/icons/info-filled.svg", "We expect this notification to have an icon." ); diff --git a/devtools/client/responsive/test/browser/browser_screenshot_button_warning.js b/devtools/client/responsive/test/browser/browser_screenshot_button_warning.js index 46d0371f9858..f7940b3cbf17 100644 --- a/devtools/client/responsive/test/browser/browser_screenshot_button_warning.js +++ b/devtools/client/responsive/test/browser/browser_screenshot_button_warning.js @@ -46,7 +46,7 @@ addRDMTask( const notificationEl = box.currentNotification; ok(notificationEl, "Notification should be visible"); is( - notificationEl.messageText.textContent, + notificationEl.messageText.textContent.trim(), "The device pixel ratio was reduced to 1 as the resulting image was too large", "The expected warning was displayed" ); diff --git a/dom/media/doctor/test/browser/browser_decoderDoctor.js b/dom/media/doctor/test/browser/browser_decoderDoctor.js index ece4b453eb5d..3586515208fb 100644 --- a/dom/media/doctor/test/browser/browser_decoderDoctor.js +++ b/dom/media/doctor/test/browser/browser_decoderDoctor.js @@ -112,7 +112,7 @@ async function test_decoder_doctor_notification( label = await document.l10n.formatValue(label.l10nId); } if (isLink) { - let link = notification.messageText.querySelector("a"); + let link = notification.supportLinkEls[0]; if (link) { // Seems to be a Windows specific quirk, but without this // mutation observer the notification.messageText.textContent @@ -126,13 +126,13 @@ async function test_decoder_doctor_notification( } } is( - notification.messageText.textContent, - notificationMessage + (isLink && label ? ` ${label}` : ""), + notification.messageText.textContent.trim(), + notificationMessage, "notification message should match expectation" ); let button = notification.buttonContainer.querySelector("button"); - let link = notification.messageText.querySelector("a"); + let link = notification.supportLinkEls[0]; if (!label) { ok(!button, "There should not be a button"); ok(!link, "There should not be a link"); @@ -141,7 +141,7 @@ async function test_decoder_doctor_notification( if (isLink) { ok(!button, "There should not be a button"); - is(link.innerText, label, `notification link should be '${label}'`); + is(link.textContent, label, `notification link should be '${label}'`); ok( !link.hasAttribute("accesskey"), "notification link should not have accesskey" diff --git a/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py b/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py index 4bc8d1219cff..2489542381e3 100644 --- a/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py +++ b/testing/firefox-ui/tests/functional/safebrowsing/test_notification.py @@ -123,7 +123,7 @@ class TestSafeBrowsingNotificationBar(WindowManagerMixin, MarionetteTestCase): message = notification_box.find_element( By.CSS_SELECTOR, "notification-message[value=blocked-badware-page]" ) - button = message.get_property("closeButton") + button = message.get_property("closeButtonEl") button.click() Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( diff --git a/toolkit/content/jar.mn b/toolkit/content/jar.mn index 0462eaea14c1..09ab9f1fb6e0 100644 --- a/toolkit/content/jar.mn +++ b/toolkit/content/jar.mn @@ -106,6 +106,7 @@ toolkit.jar: content/global/elements/moz-toggle.css (widgets/moz-toggle/moz-toggle.css) content/global/elements/moz-toggle.mjs (widgets/moz-toggle/moz-toggle.mjs) content/global/elements/named-deck.js (widgets/named-deck.js) + content/global/elements/infobar.css (widgets/infobar.css) content/global/elements/notificationbox.js (widgets/notificationbox.js) content/global/elements/panel.js (widgets/panel.js) content/global/elements/panel-item.css (widgets/panel-list/panel-item.css) diff --git a/toolkit/content/tests/chrome/test_bug457632.xhtml b/toolkit/content/tests/chrome/test_bug457632.xhtml index 7f94eaef09a0..12267c1decfd 100644 --- a/toolkit/content/tests/chrome/test_bug457632.xhtml +++ b/toolkit/content/tests/chrome/test_bug457632.xhtml @@ -28,14 +28,14 @@ function completeAnimation(nextTest) { setTimeout(completeAnimation, 50, nextTest); } -function test() { +async function test() { SimpleTest.waitForExplicitFinish(); gNotificationBox = new MozElements.NotificationBox(e => { document.getElementById("nb").appendChild(e); }); is(gNotificationBox.allNotifications.length, 0, "There should be no initial notifications"); - gNotificationBox.appendNotification("notification1", + await gNotificationBox.appendNotification("notification1", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); is(gNotificationBox.allNotifications.length, 1, "Notification exists while animating in"); let notification = gNotificationBox.getNotificationWithValue("notification1"); @@ -46,7 +46,7 @@ function test() { } // Tests that a notification that is fully animated in gets removed immediately -function test1() { +async function test1() { let notification = gNotificationBox.getNotificationWithValue("notification1"); gNotificationBox.removeNotification(notification); notification = gNotificationBox.getNotificationWithValue("notification1"); @@ -59,8 +59,8 @@ function test1() { } // Tests that a notification that is animating in gets removed immediately -function test2() { - let notification = gNotificationBox.appendNotification("notification2", +async function test2() { + let notification = await gNotificationBox.appendNotification("notification2", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); gNotificationBox.removeNotification(notification); notification = gNotificationBox.getNotificationWithValue("notification2"); @@ -74,10 +74,10 @@ function test2() { } // Tests that a background notification goes away immediately -function test3() { - let notification = gNotificationBox.appendNotification("notification3", +async function test3() { + let notification = await gNotificationBox.appendNotification("notification3", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); - let notification2 = gNotificationBox.appendNotification("notification4", + let notification2 = await gNotificationBox.appendNotification("notification4", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); is(gNotificationBox.allNotifications.length, 2, "Test 3 should show 2 notifications present"); gNotificationBox.removeNotification(notification); @@ -96,10 +96,10 @@ function test3() { } // Tests that a foreground notification hiding a background one goes away -function test4() { - let notification = gNotificationBox.appendNotification("notification5", +async function test4() { + let notification = await gNotificationBox.appendNotification("notification5", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); - let notification2 = gNotificationBox.appendNotification("notification6", + let notification2 = await gNotificationBox.appendNotification("notification6", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); gNotificationBox.removeNotification(notification2); notification2 = gNotificationBox.getNotificationWithValue("notification6"); @@ -118,10 +118,10 @@ function test4() { } // Tests that removeAllNotifications gets rid of everything -function test5() { - let notification = gNotificationBox.appendNotification("notification7", +async function test5() { + let notification = await gNotificationBox.appendNotification("notification7", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); - let notification2 = gNotificationBox.appendNotification("notification8", + let notification2 = await gNotificationBox.appendNotification("notification8", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); gNotificationBox.removeAllNotifications(); notification = gNotificationBox.getNotificationWithValue("notification7"); @@ -131,7 +131,7 @@ function test5() { ok(!gNotificationBox.currentNotification, "Test 5 said there was still a current notification"); is(gNotificationBox.allNotifications.length, 0, "Test 5 should show 0 notifications present"); - gNotificationBox.appendNotification("notification9", + await gNotificationBox.appendNotification("notification9", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); // Wait for the notificaton to finish displaying @@ -139,7 +139,7 @@ function test5() { } // Tests whether removing an already removed notification doesn't break things -function test6() { +async function test6() { let notification = gNotificationBox.getNotificationWithValue("notification9"); ok(notification, "Test 6 should have an initial notification"); gNotificationBox.removeNotification(notification); @@ -147,7 +147,7 @@ function test6() { ok(!gNotificationBox.currentNotification, "Test 6 shouldn't be any current notification"); is(gNotificationBox.allNotifications.length, 0, "Test 6 allNotifications.length should be 0"); - notification = gNotificationBox.appendNotification("notification10", + notification = await gNotificationBox.appendNotification("notification10", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); is(notification, gNotificationBox.currentNotification, "Test 6 should have made the current notification"); gNotificationBox.removeNotification(notification); diff --git a/toolkit/content/tests/chrome/test_bug509732.xhtml b/toolkit/content/tests/chrome/test_bug509732.xhtml index c886a05fd134..7e340322b296 100644 --- a/toolkit/content/tests/chrome/test_bug509732.xhtml +++ b/toolkit/content/tests/chrome/test_bug509732.xhtml @@ -20,7 +20,7 @@ var gNotificationBox; // Tests that a notification that is added in an hidden box didn't throw the animation -function test() { +async function test() { SimpleTest.waitForExplicitFinish(); gNotificationBox = new MozElements.NotificationBox(e => { document.getElementById("nb").appendChild(e); @@ -28,7 +28,7 @@ function test() { is(gNotificationBox.allNotifications.length, 0, "There should be no initial notifications"); - gNotificationBox.appendNotification("notification1", + await gNotificationBox.appendNotification("notification1", { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); is(gNotificationBox.allNotifications.length, 1, "Notification exists"); diff --git a/toolkit/content/tests/chrome/test_notificationbox.xhtml b/toolkit/content/tests/chrome/test_notificationbox.xhtml index 483adec77a30..8de985175a29 100644 --- a/toolkit/content/tests/chrome/test_notificationbox.xhtml +++ b/toolkit/content/tests/chrome/test_notificationbox.xhtml @@ -138,18 +138,15 @@ function testtag_notification_eventCallback(expectedEvents, ntf, testName) var tests = [ { - test(nb, ntf) { - ntf = nb.appendNotification("mutable", { + async test(nb, ntf) { + ntf = await nb.appendNotification("mutable", { label: "Original", priority: nb.PRIORITY_INFO_LOW, }, testtag_notificationbox_buttons); ntf.label = "Changed string"; - SimpleTest.is(ntf.messageText.textContent, "Changed string", "set notification label with string"); - - ntf.label = { "l10n-id": "foo-bar" }; - const message = ntf.messageText.querySelector("span"); - SimpleTest.is(message.dataset.l10nId, "foo-bar", "set notification label with l10n id"); + await ntf.updateComplete; + SimpleTest.is(ntf.messageText.textContent.trim(), "Changed string", "set notification label with string"); return ntf; }, result(nb, ntf) { @@ -162,8 +159,8 @@ var tests = label attribute set correctly. */ { - test(nb, ntf) { - ntf = nb.appendNotification("note", { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_LOW, @@ -183,8 +180,8 @@ var tests = Ensures that buttons created with the "l10n-id" parameter have their "l10n-id" assigned correctly. */ - test(nb, ntf) { - ntf = nb.appendNotification("note", { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_LOW, @@ -200,9 +197,9 @@ var tests = } }, { - test(nb, ntf) { + async test(nb, ntf) { // append a new notification - ntf = nb.appendNotification("note", { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_LOW, @@ -235,9 +232,9 @@ var tests = } }, { - test(nb, ntf) { + async test(nb, ntf) { // append a new notification, but now with an event callback - ntf = nb.appendNotification("note", { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_LOW, @@ -265,8 +262,8 @@ var tests = } }, { - test(nb, ntf) { - ntf = nb.appendNotification("note", { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_MEDIUM, @@ -299,8 +296,8 @@ var tests = } }, { - test(nb, ntf) { - ntf = nb.appendNotification("note", { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_WARNING_LOW, @@ -321,13 +318,13 @@ var tests = }, { repeat: true, - test(nb, arr) { + async test(nb, arr) { var idx = arr[0]; var ntf = arr[1]; switch (idx) { case 1: // append a new notification - ntf = nb.appendNotification("note", { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_LOW, @@ -373,9 +370,9 @@ var tests = } }, { - test(nb, ntf) { + async test(nb, ntf) { // append another notification - ntf = nb.appendNotification("note", { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_MEDIUM, @@ -402,8 +399,8 @@ var tests = } }, { - test(nb, ntf) { - ntf = nb.appendNotification("note", { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_HIGH, @@ -419,8 +416,8 @@ var tests = } }, { - test(nb, ntf) { - ntf = nb.appendNotification("note", { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_LOW, @@ -437,7 +434,7 @@ var tests = SimpleTest.is(button.localName, "button", "button is a button"); SimpleTest.ok(!button.href, "button href is not set"); - let link = ntf.messageText.lastElementChild; + let link = ntf.querySelector(".notification-link"); SimpleTest.is(link.localName, "label", "link is a label"); SimpleTest.is(link.href, "about:mozilla", "link href is correct"); @@ -445,9 +442,9 @@ var tests = } }, { - test(nb, ntf) { + async test(nb, ntf) { // append a new notification - ntf = nb.appendNotification("note", { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_LOW, @@ -473,20 +470,21 @@ var tests = } }, { - test(nb, ntf) { - ntf = nb.appendNotification("note", { + async test(nb, ntf) { + ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_LOW, eventCallback: notification_eventCallback, }, testtag_notificationbox_supportpage); + await ntf.updateComplete; SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append support page notification"); return ntf; }, result(nb, ntf) { testtag_notificationbox_State(nb, "append link with callback", ntf, 1); - let link = ntf.messageText.firstElementChild; + let link = ntf.querySelector(".notification-link"); SimpleTest.is(link.localName, "a", "link 1 is an anchor"); SimpleTest.is(link.dataset.l10nId, "moz-support-link-text", "link 1 Fluent ID is set"); SimpleTest.ok(link.href.endsWith("/test1"), "link 1 href is set"); @@ -512,20 +510,20 @@ var tests = } }, { - test(nb, unused) { + async test(nb, unused) { // add a number of notifications and check that they are added in order - nb.appendNotification("4", { label: "Four", priority: nb.PRIORITY_INFO_HIGH }, + await nb.appendNotification("4", { label: "Four", priority: nb.PRIORITY_INFO_HIGH }, testtag_notificationbox_buttons); - nb.appendNotification("7", { label: "Seven", priority: nb.PRIORITY_WARNING_HIGH }, + await nb.appendNotification("7", { label: "Seven", priority: nb.PRIORITY_WARNING_HIGH }, testtag_notificationbox_buttons); - nb.appendNotification("2", { label: "Two", priority: nb.PRIORITY_INFO_LOW }); - nb.appendNotification("8", { label: "Eight", priority: nb.PRIORITY_CRITICAL_LOW }); - nb.appendNotification("5", { label: "Five", priority: nb.PRIORITY_WARNING_LOW }); - nb.appendNotification("6", { label: "Six", priority: nb.PRIORITY_WARNING_HIGH }); - nb.appendNotification("1", { label: "One", priority: nb.PRIORITY_INFO_LOW }); - nb.appendNotification("9", { label: "Nine", priority: nb.PRIORITY_CRITICAL_MEDIUM }); - let ntf = nb.appendNotification("10", { label: "Ten", priority: nb.PRIORITY_CRITICAL_HIGH }); - nb.appendNotification("3", { label: "Three", priority: nb.PRIORITY_INFO_MEDIUM }); + await nb.appendNotification("2", { label: "Two", priority: nb.PRIORITY_INFO_LOW }); + await nb.appendNotification("8", { label: "Eight", priority: nb.PRIORITY_CRITICAL_LOW }); + await nb.appendNotification("5", { label: "Five", priority: nb.PRIORITY_WARNING_LOW }); + await nb.appendNotification("6", { label: "Six", priority: nb.PRIORITY_WARNING_HIGH }); + await nb.appendNotification("1", { label: "One", priority: nb.PRIORITY_INFO_LOW }); + await nb.appendNotification("9", { label: "Nine", priority: nb.PRIORITY_CRITICAL_MEDIUM }); + let ntf = await nb.appendNotification("10", { label: "Ten", priority: nb.PRIORITY_CRITICAL_HIGH }); + await nb.appendNotification("3", { label: "Three", priority: nb.PRIORITY_INFO_MEDIUM }); return ntf; }, result(nb, ntf) { @@ -558,16 +556,16 @@ var tests = } }, { - test(nb, ntf) { + async test(nb, ntf) { var exh = false; try { - nb.appendNotification("no", { label: "no", priority: -1 }); + await nb.appendNotification("no", { label: "no", priority: -1 }); } catch (ex) { exh = true; } SimpleTest.is(exh, true, "appendNotification priority too low"); exh = false; try { - nb.appendNotification("no", { label: "no", priority: 11 }); + await nb.appendNotification("no", { label: "no", priority: 11 }); } catch (ex) { exh = true; } SimpleTest.is(exh, true, "appendNotification priority too high"); @@ -579,8 +577,8 @@ var tests = var appendPriorityTests = [ { - test(nb, priority) { - let ntf = nb.appendNotification("note", { + async test(nb, priority) { + let ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority, @@ -598,9 +596,9 @@ var appendPriorityTests = [ nb.removeCurrentNotification(); return priority; }, - result(nb, priority) { + async result(nb, priority) { if (priority == nb.PRIORITY_CRITICAL_HIGH) { - let ntf = nb.appendNotification("note", { + let ntf = await nb.appendNotification("note", { label: "Notification", image: "happy.png", priority: nb.PRIORITY_INFO_LOW, @@ -623,7 +621,7 @@ function testtag_notificationbox_State(nb, testid, expecteditem, expectedcount) function testtag_notification_State(nb, ntf, testid, label, value, image, priority) { - is(ntf.messageText.textContent, label, testid + " notification label"); + is(ntf.messageText.textContent.trim(), label, testid + " notification label"); is(ntf.getAttribute("value"), value, testid + " notification value"); is(ntf.priority, priority, testid + " notification priority"); @@ -654,7 +652,7 @@ function testtag_notification_State(nb, ntf, testid, label, value, image, priori critical: "chrome://global/skin/icons/error.svg", }; let icon = icons[type]; - is(getComputedStyle(ntf.messageImage, "::after").backgroundImage, `url("${icon}")`, "notification image is set"); + is(ntf.messageImage.src, icon, "notification image is set"); } function checkPopupTest(nb, ntf) @@ -697,7 +695,7 @@ function checkPopupClosed() * function as its arg. The result function may also return a value which * will be passed to the next repetition or the next test in the array. */ -function runTimedTests(tests, idx, element, arg) +async function runTimedTests(tests, idx, element, arg) { if (idx >= 0 && "result" in tests[idx]) arg = tests[idx].result(element, arg); @@ -707,7 +705,7 @@ function runTimedTests(tests, idx, element, arg) idx++; if (idx < tests.length) { - var result = tests[idx].test(element, arg); + let result = await tests[idx].test(element, arg); setTimeout(runTimedTestsWait, 50, tests, idx, element, result); } } diff --git a/toolkit/content/widgets/infobar.css b/toolkit/content/widgets/infobar.css new file mode 100644 index 000000000000..ee811818b524 --- /dev/null +++ b/toolkit/content/widgets/infobar.css @@ -0,0 +1,99 @@ +/* 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/. */ + +:host(.infobar) { + --info-bar-background-color: light-dark(var(--color-white), rgb(66, 65, 77)); + --info-bar-text-color: light-dark(var(--color-gray-100), var(--color-gray-05)); + position: relative; + + &::before { + content: ""; + display: block; + width: 2px; + position: absolute; + top: 0; + inset-inline-start: 0; + height: 100%; + border-start-start-radius: 4px; + border-end-start-radius: 4px; + } + + .container { + /* Don't let lwthemes set a text-shadow. */ + text-shadow: none; + padding-block: 3px; + align-items: center; + } + + .content { + gap: 0 12px; + height: fit-content; + } + + .close { + margin-block: 4px; + margin-inline-start: 8px; + background-size: 12px; + height: 24px; + width: 24px; + align-self: flex-start; + } +} + +@media (prefers-contrast) { + :host(.infobar)::before { + background-color: CanvasText; + } +} + +@media not (prefers-contrast) { + :host(.infobar) { + box-shadow: 0 1px 2px rgba(58, 57, 68, 0.1); + background-color: var(--info-bar-background-color); + color: var(--info-bar-text-color); + + &::before { + background-image: linear-gradient(0deg, #9059ff 0%, #ff4aa2 52.08%, #ffbd4f 100%); + } + } +} + +:host([message-bar-type=infobar]:first-of-type) { + margin-top: 4px; +} + +:host([message-bar-type=infobar]) { + margin: 0 4px 4px; +} + +::slotted(.notification-button-container) { + gap: 8px; + display: inline-flex; +} + +::slotted(.text-link) { + margin: 0 !important; +} + +img.inline-icon { + /* Align inline icon images in the message content */ + vertical-align: middle; + /* Ensure they get the right fill color. */ + -moz-context-properties: fill; + fill: currentColor; +} + +strong { + font-weight: var(--font-weight-bold); +} + +/* type="system" infobar styles */ + +:host([type=system]) .icon { + display: none; +} + +:host([type=system]) .content { + margin-inline-start: 0; +} diff --git a/toolkit/content/widgets/message-bar.css b/toolkit/content/widgets/message-bar.css index 616b62f2118c..eddc5a3ae6c6 100644 --- a/toolkit/content/widgets/message-bar.css +++ b/toolkit/content/widgets/message-bar.css @@ -13,10 +13,6 @@ --close-icon-size: 28px; } -:host([message-bar-type=infobar]) { - --close-icon-size: 28px; -} - :host { --message-bar-background-color: var(--in-content-box-info-background); --message-bar-text-color: var(--in-content-text-color); @@ -28,7 +24,7 @@ --message-bar-icon-url: var(--warn-icon-url); } -:host([type=success]:not([message-bar-type=infobar])) { +:host([type=success]) { --message-bar-icon-url: var(--success-icon-url); } @@ -125,26 +121,23 @@ min-height: auto; width: var(--close-icon-size); height: var(--close-icon-size); - margin: 0; padding: 0; flex-shrink: 0; + margin: 4px 8px; + background-size: 12px; } @media (prefers-contrast) { :host { border-color: CanvasText; } - - .container.infobar::before { - background-color: CanvasText; - } } @media not (prefers-contrast) { /* MessageBar colors by message type */ /* Colors from: https://design.firefox.com/photon/components/message-bars.html#type-specific-style */ - :host([type=warning]:not([message-bar-type=infobar])) { + :host([type=warning]) { /* Ensure colors within the bar are adjusted and controls are readable */ color-scheme: light; @@ -158,7 +151,7 @@ --close-fill-color: var(--message-bar-text-color); } - :host([type=success]:not([message-bar-type=infobar])) { + :host([type=success]) { /* Ensure colors within the bar are adjusted and controls are readable */ color-scheme: light; @@ -170,7 +163,7 @@ --in-content-button-background-active: var(--green-80); } - :host([type=error]:not([message-bar-type=infobar])) { + :host([type=error]) { --message-bar-background-color: var(--red-60); --message-bar-text-color: #ffffff; @@ -191,15 +184,6 @@ color: rgb(226,40,80); } - .container.infobar { - box-shadow: 0 1px 2px rgba(58, 57, 68, 0.1); - background: var(--in-content-page-background); - } - - .container.infobar::before { - background-image: linear-gradient(0deg, #9059ff 0%, #ff4aa2 52.08%, #ffbd4f 100%); - } - .close { fill: var(--close-fill-color); } @@ -207,10 +191,6 @@ @media (prefers-color-scheme: dark) { /* Don't set the background in prefers-contrast mode or macOS can end up * with black on black text. */ - .container.infobar { - background: rgb(66,65,77); - } - :host([type=info]) .icon { color: rgb(128,235,255); } @@ -225,81 +205,6 @@ } } -:host([message-bar-type=infobar]:first-of-type) { - margin-top: 4px; -} - -:host([message-bar-type=infobar]) { - margin: 0 4px 4px; -} - -.container.infobar { - /* Don't let lwthemes set a text-shadow. */ - text-shadow: none; - padding: 0; -} - -.container.infobar::before { - content: ""; - display: block; - width: 2px; - position: absolute; - top: 0; - inset-inline-start: 0; - height: 100%; - border-start-start-radius: 4px; - border-end-start-radius: 4px; -} - -.container.infobar { - align-items: flex-start; -} - -/* Infobars styling. */ -.notification-content { - margin: 0; - margin-inline-start: 8px; - padding-block: 3px; -} - -.notification-message { - padding-block: 8px; - margin-inline-end: 20px; - /* Prevent misalignment issue in the infobar on Linux */ - @media (-moz-platform: linux) { - padding-block-start: 6px; - } -} - -.notification-button-container, -.notification-message { - display: inline-block; - vertical-align: middle; -} - -.container.infobar > .notification-content > .notification-message img.inline-icon { - /* Align inline icon images in the message content */ - vertical-align: middle; - /* Ensure they get the right fill color. */ - -moz-context-properties: fill; - fill: currentColor; -} - -.close { - margin: 4px 8px; - background-size: 12px; -} - -.notification-button { - align-items: center; - margin: 0 4px; -} - -.notification-button:first-of-type { - /* When the buttons wrap to their own line we want to match the 8px on the message. */ - margin-inline-start: 0; -} - strong { font-weight: 600; } @@ -308,20 +213,6 @@ strong { cursor: pointer; } -.infobar > .icon { - padding: 0; - margin: 10px 0; -} - -.infobar > .icon, -:host([type=system]) .notification-content { - margin-inline-start: 16px; -} - -:host([type=system]) .icon { - display: none; -} - @keyframes spin { from { transform: rotate(0); } to { transform: rotate(360deg); } diff --git a/toolkit/content/widgets/moz-message-bar/moz-message-bar.css b/toolkit/content/widgets/moz-message-bar/moz-message-bar.css index e80916d711d3..6d3500998244 100644 --- a/toolkit/content/widgets/moz-message-bar/moz-message-bar.css +++ b/toolkit/content/widgets/moz-message-bar/moz-message-bar.css @@ -177,26 +177,27 @@ :host([type=warning]) { --message-bar-background-color: var(--color-background-warning); + + .icon { + --message-bar-icon-color: var(--icon-color-warning); + } } :host([type=success]) { --message-bar-background-color: var(--color-background-success); + + .icon { + --message-bar-icon-color: var(--icon-color-success); + } } - :host([type=error]) { + :host([type=error]), + :host([type=critical]) { --message-bar-background-color: var(--color-background-critical); - } - :host([type=success]) .icon { - --message-bar-icon-color: var(--icon-color-success); - } - - :host([type=warning]) .icon { - --message-bar-icon-color: var(--icon-color-warning); - } - - :host([type=error]) .icon { - --message-bar-icon-color: var(--icon-color-critical); + .icon { + --message-bar-icon-color: var(--icon-color-critical); + } } .close { diff --git a/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs b/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs index acd3638e2351..58f41c28e4a9 100644 --- a/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs +++ b/toolkit/content/widgets/moz-message-bar/moz-message-bar.mjs @@ -22,6 +22,10 @@ const messageTypeToIconData = { iconSrc: "chrome://global/skin/icons/error.svg", l10nId: "moz-message-bar-icon-error", }, + critical: { + iconSrc: "chrome://global/skin/icons/error.svg", + l10nId: "moz-message-bar-icon-error", + }, }; /** @@ -33,6 +37,8 @@ const messageTypeToIconData = { * @property {string} heading - The heading of the message. * @property {string} message - The message text. * @property {boolean} dismissable - Whether or not the element is dismissable. + * @property {string} messageL10nId - l10n ID for the message. + * @property {string} messageL10nArgs - Any args needed for the message l10n ID. * @fires message-bar:close * Custom event indicating that message bar was closed. * @fires message-bar:user-dismissed @@ -44,6 +50,7 @@ export default class MozMessageBar extends MozLitElement { actionsSlotEl: "slot[name=actions]", actionsEl: ".actions", closeButtonEl: "button.close", + supportLinkSlotEl: "slot[name=support-link]", }; static properties = { @@ -51,6 +58,8 @@ export default class MozMessageBar extends MozLitElement { heading: { type: String }, message: { type: String }, dismissable: { type: Boolean }, + messageL10nId: { type: String }, + messageL10nArgs: { type: String }, }; constructor() { @@ -75,6 +84,10 @@ export default class MozMessageBar extends MozLitElement { this.dispatchEvent(new CustomEvent("message-bar:close")); } + get supportLinkEls() { + return this.supportLinkSlotEl.assignedElements(); + } + iconTemplate() { let iconData = messageTypeToIconData[this.type]; if (iconData) { @@ -126,7 +139,15 @@ export default class MozMessageBar extends MozLitElement {
${this.headingTemplate()}
- ${ifDefined(this.message)} + + ${this.message} + diff --git a/toolkit/content/widgets/notificationbox.js b/toolkit/content/widgets/notificationbox.js index 889001a0cfed..f23fb03a7466 100644 --- a/toolkit/content/widgets/notificationbox.js +++ b/toolkit/content/widgets/notificationbox.js @@ -144,7 +144,7 @@ * * @return The element that is shown. */ - appendNotification(aType, aNotification, aButtons) { + async appendNotification(aType, aNotification, aButtons) { if ( aNotification.priority < this.PRIORITY_SYSTEM || aNotification.priority > this.PRIORITY_CRITICAL_HIGH @@ -157,12 +157,19 @@ MozXULElement.insertFTLIfNeeded("toolkit/global/notification.ftl"); // Create the Custom Element and connect it to the document immediately. - var newitem; + let newitem; if (!aNotification.notificationIs) { if (!customElements.get("notification-message")) { // There's some weird timing stuff when this element is created at // script load time, we don't need it until now anyway so be lazy. - createNotificationMessageElement(); + // Wrapped in a try/catch to handle rare cases where we start creating + // a notification but then the window gets closed/goes away. + try { + await createNotificationMessageElement(); + } catch (err) { + console.warn(err); + throw err; + } } newitem = document.createElement("notification-message"); newitem.setAttribute("message-bar-type", "infobar"); @@ -182,8 +189,10 @@ this.stack.append(newitem); } - // Custom notification classes may not have the messageText property. - if (newitem.messageText) { + if (newitem.localName === "notification-message" && aNotification.label) { + newitem.label = aNotification.label; + } else if (newitem.messageText) { + // Custom notification classes may not have the messageText property. // Can't use instanceof in case this was created from a different document: if ( aNotification.label && @@ -241,6 +250,10 @@ newitem.style.top = "100%"; newitem.style.marginTop = "-15px"; newitem.style.opacity = "0"; + + // Ensure the DOM has been created for the Lit-based notification-message + // element so that we add the .animated class + it animates as expected. + await newitem.updateComplete; this._showNotification(newitem, true); // Fire event for accessibility APIs @@ -607,53 +620,56 @@ customElements.define("notification", MozElements.Notification); - function createNotificationMessageElement() { - // Get a reference to MessageBarElement from a created element so the import - // gets handled automatically if needed. - class NotificationMessage extends document.createElement("message-bar") - .constructor { + async function createNotificationMessageElement() { + await window.ensureCustomElements("moz-message-bar"); + let MozMessageBar = customElements.get("moz-message-bar"); + class NotificationMessage extends MozMessageBar { + static queries = { + ...MozMessageBar.queries, + messageText: ".message", + messageImage: ".icon", + }; + constructor() { super(); this.persistence = 0; this.priority = 0; this.timeout = 0; this.telemetry = null; + this.dismissable = true; this._shown = false; + + this.addEventListener("click", this); + this.addEventListener("command", this); } connectedCallback() { - this.toggleAttribute("dismissable", true); - this.closeButton.classList.add("notification-close"); + super.connectedCallback(); + this.#setStyles(); - this.container = this.shadowRoot.querySelector(".container"); - this.container.classList.add("infobar"); + this.classList.add("infobar"); this.setAlertRole(); - let messageContent = this.shadowRoot.querySelector(".content"); - messageContent.classList.add("notification-content"); - - // Remove the , API surface is `set label()` and `setButtons()`. - messageContent.textContent = ""; - - // A 'label' allows screen readers to detect the text of the alert. - this.messageText = document.createElement("label"); - this.messageText.classList.add("notification-message"); this.buttonContainer = document.createElement("span"); this.buttonContainer.classList.add("notification-button-container"); - - this.messageImage = this.shadowRoot.querySelector(".icon"); - - messageContent.append(this.messageText, this.buttonContainer); - this.shadowRoot.addEventListener("click", this); - this.shadowRoot.addEventListener("command", this); + this.buttonContainer.setAttribute("slot", "actions"); + this.appendChild(this.buttonContainer); } disconnectedCallback() { + super.disconnectedCallback(); if (this.eventCallback) { this.eventCallback("disconnected"); } } + #setStyles() { + let style = document.createElement("link"); + style.rel = "stylesheet"; + style.href = "chrome://global/content/elements/infobar.css"; + this.renderRoot.append(style); + } + _doTelemetry(type) { if ( this.telemetry && @@ -686,10 +702,10 @@ setAlertRole() { // Wait a little for this to render before setting the role for more // consistent alerts to screen readers. - this.container.removeAttribute("role"); + this.removeAttribute("role"); window.requestAnimationFrame(() => { window.requestAnimationFrame(() => { - this.container.setAttribute("role", "alert"); + this.setAttribute("role", "alert"); }); }); } @@ -736,18 +752,10 @@ */ set label(value) { if (value && typeof value == "object" && "l10n-id" in value) { - const message = document.createElement("span"); - document.l10n.setAttributes( - message, - value["l10n-id"], - value["l10n-args"] - ); - while (this.messageText.firstChild) { - this.messageText.firstChild.remove(); - } - this.messageText.appendChild(message); + this.messageL10nId = value["l10n-id"]; + this.messageL10nArgs = value["l10n-args"]; } else { - this.messageText.textContent = value; + this.message = value; } this.setAlertRole(); } @@ -777,7 +785,11 @@ "button", button.is ? { is: button.is } : {} ); - buttonElem.classList.add("notification-button", "small-button"); + buttonElem.classList.add( + "notification-button", + "small-button", + "footer-button" + ); if (button.primary) { buttonElem.classList.add("primary"); @@ -794,7 +806,8 @@ } if (link) { - this.messageText.append(new Text(" "), buttonElem); + buttonElem.setAttribute("slot", "support-link"); + this.appendChild(buttonElem); } else { this.buttonContainer.appendChild(buttonElem); } @@ -811,6 +824,8 @@ super.dismiss(); } } - customElements.define("notification-message", NotificationMessage); + if (!customElements.get("notification-message")) { + customElements.define("notification-message", NotificationMessage); + } } } diff --git a/toolkit/themes/shared/design-system/tokens-shared.css b/toolkit/themes/shared/design-system/tokens-shared.css index a2b103aae06b..86b14f1daa68 100644 --- a/toolkit/themes/shared/design-system/tokens-shared.css +++ b/toolkit/themes/shared/design-system/tokens-shared.css @@ -93,6 +93,7 @@ /** Size **/ --size-item-small: 16px; + --size-item-medium: 28px; --size-item-large: 32px; /** Spacing **/ diff --git a/toolkit/themes/shared/global-shared.css b/toolkit/themes/shared/global-shared.css index ad573e4530cc..3d7ea499c54f 100644 --- a/toolkit/themes/shared/global-shared.css +++ b/toolkit/themes/shared/global-shared.css @@ -225,12 +225,19 @@ button.text-link .button-text { color: var(--button-color, inherit); background-color: var(--button-bgcolor, color-mix(in srgb, currentColor 13%, transparent)); padding: .45em 1em; - min-height: 32px; + min-height: var(--size-item-large); font-weight: 600; min-width: 0; margin-inline: 8px 0; margin-bottom: 0; + &.small-button { + margin: 0; + min-height: var(--size-item-medium); + padding: .6em 1em; + font-size: var(--font-size-small); + } + &[disabled] { opacity: 0.4; } @@ -247,7 +254,8 @@ button.text-link .button-text { &:hover:active { background-color: var(--button-active-bgcolor, color-mix(in srgb, currentColor 30%, transparent)); } - &[default] { + &[default], + &.primary { color: var(--button-primary-color); background-color: var(--button-primary-bgcolor);