Bug 1845150 - Use moz-message-bar instead of message-bar in notificationbox.js r=webdriver-reviewers,desktop-theme-reviewers,media-playback-reviewers,karlt,whimboo,tgiles,dao,devtools-reviewers

This patch updates the `NotificationMessage` element in `notificationbox.js` so that it extends our newer `moz-message-bar` component instead of the deprecated `message-bar` component. Many of the changes are just dealing with the implications of making things async (so that we can ensure `moz-message-bar.mjs` gets imported). I tried to break out places where I modified related code and tests into separate patches to mitigate some of the review pain here.

This patch solves a longstanding issue where we were loading `in-content/common-shared.css` in the chrome since it gets used by the `message-bar` element. It also makes some small visual changes to our infobars (slight outline, icon colors, adds a bit of spacing).

Differential Revision: https://phabricator.services.mozilla.com/D189872
This commit is contained in:
Hanna Jones 2024-01-10 18:55:29 +00:00
Родитель 9c1d13a6a6
Коммит 614f900ec6
25 изменённых файлов: 392 добавлений и 341 удалений

Просмотреть файл

@ -178,11 +178,20 @@ export class PluginParent extends JSWindowActorParent {
buttons.push(submitButton); 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( let messageString = lazy.gNavigatorBundle.formatStringFromName(
"crashedpluginsMessage.title", "crashedpluginsMessage.title",
[report.pluginName] [report.pluginName]
); );
notification = notificationBox.appendNotification( notificationBox.appendNotification(
"plugin-crashed", "plugin-crashed",
{ {
label: messageString, label: messageString,
@ -191,21 +200,5 @@ export class PluginParent extends JSWindowActorParent {
}, },
buttons 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);
} }
} }

Просмотреть файл

@ -40,7 +40,7 @@ var gDataNotificationInfoBar = {
return gNotificationBox.getNotificationWithValue(name); return gNotificationBox.getNotificationWithValue(name);
}, },
_displayDataPolicyInfoBar(request) { async _displayDataPolicyInfoBar(request) {
if (this._getDataReportingNotification()) { if (this._getDataReportingNotification()) {
return; return;
} }
@ -59,7 +59,7 @@ var gDataNotificationInfoBar = {
]; ];
this._log.info("Creating data reporting policy notification."); this._log.info("Creating data reporting policy notification.");
gNotificationBox.appendNotification( await gNotificationBox.appendNotification(
this._DATA_REPORTING_NOTIFICATION, this._DATA_REPORTING_NOTIFICATION,
{ {
label: { label: {

Просмотреть файл

@ -1106,7 +1106,7 @@ const gStoragePressureObserver = {
} }
messageFragment.appendChild(message); messageFragment.appendChild(message);
gNotificationBox.appendNotification( await gNotificationBox.appendNotification(
NOTIFICATION_VALUE, NOTIFICATION_VALUE,
{ {
label: messageFragment, label: messageFragment,
@ -1122,7 +1122,7 @@ const gStoragePressureObserver = {
}; };
var gPopupBlockerObserver = { var gPopupBlockerObserver = {
handleEvent(aEvent) { async handleEvent(aEvent) {
if (aEvent.originalTarget != gBrowser.selectedBrowser) { if (aEvent.originalTarget != gBrowser.selectedBrowser) {
return; return;
} }
@ -1158,23 +1158,31 @@ var gPopupBlockerObserver = {
let notificationBox = gBrowser.getNotificationBox(); let notificationBox = gBrowser.getNotificationBox();
let notification = let notification =
notificationBox.getNotificationWithValue("popup-blocked"); notificationBox.getNotificationWithValue("popup-blocked") ||
(await this.notificationPromise);
if (notification) { if (notification) {
notification.label = label; notification.label = label;
} else { } else {
const image = "chrome://browser/skin/notification-icons/popup.svg"; const image = "chrome://browser/skin/notification-icons/popup.svg";
const priority = notificationBox.PRIORITY_INFO_MEDIUM; const priority = notificationBox.PRIORITY_INFO_MEDIUM;
notificationBox.appendNotification( try {
"popup-blocked", this.notificationPromise = notificationBox.appendNotification(
{ label, image, priority }, "popup-blocked",
[ { label, image, priority },
{ [
"l10n-id": "popup-warning-button", {
popup: "blockedPopupOptions", "l10n-id": "popup-warning-button",
callback: null, 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 asciiHost = fixedURI.asciiHost;
let onLookupCompleteListener = { let onLookupCompleteListener = {
onLookupComplete(request, record, status) { async onLookupComplete(request, record, status) {
let browserRef = weakBrowser.get(); let browserRef = weakBrowser.get();
if (!Components.isSuccessCode(status) || !browserRef) { if (!Components.isSuccessCode(status) || !browserRef) {
return; return;
@ -1457,7 +1465,7 @@ var gKeywordURIFixup = {
}, },
}, },
]; ];
let notification = notificationBox.appendNotification( let notification = await notificationBox.appendNotification(
"keyword-uri-fixup", "keyword-uri-fixup",
{ {
label: message, label: message,
@ -3334,22 +3342,9 @@ function PageProxyClickHandler(aEvent) {
* us via async messaging. * us via async messaging.
*/ */
var BrowserOnClick = { var BrowserOnClick = {
ignoreWarningLink(reason, blockedInfo, browsingContext) { async ignoreWarningLink(reason, blockedInfo, browsingContext) {
let triggeringPrincipal = // Add a notify bar before allowing the user to continue through to the
blockedInfo.triggeringPrincipal || // site, so that they don't lose track after, e.g., tab switching.
_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,
});
// We can't use browser.contentPrincipal which is principal of about:blocked // We can't use browser.contentPrincipal which is principal of about:blocked
// Create one from uri with current principal origin attributes // Create one from uri with current principal origin attributes
let principal = Services.scriptSecurityManager.createContentPrincipal( let principal = Services.scriptSecurityManager.createContentPrincipal(
@ -3424,7 +3419,20 @@ var BrowserOnClick = {
// provide a URL endpoint for these reports. // 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 = { const SafeBrowsingNotificationBox = {
_currentURIBaseDomain: null, _currentURIBaseDomain: null,
show(title, buttons) { async show(title, buttons) {
let uri = gBrowser.currentURI; let uri = gBrowser.currentURI;
// start tracking host so that we know when we leave the domain // start tracking host so that we know when we leave the domain
@ -9080,7 +9088,7 @@ const SafeBrowsingNotificationBox = {
notificationBox.removeNotification(previousNotification); notificationBox.removeNotification(previousNotification);
} }
let notification = notificationBox.appendNotification( let notification = await notificationBox.appendNotification(
value, value,
{ {
label: title, label: title,

Просмотреть файл

@ -176,6 +176,7 @@ add_task(async function test_single_window() {
// Wait for the infobar to be displayed. // Wait for the infobar to be displayed.
triggerInfoBar(10 * 1000); triggerInfoBar(10 * 1000);
await alertShownPromise; await alertShownPromise;
await promiseNextTick();
Assert.equal( Assert.equal(
gNotificationBox.allNotifications.length, gNotificationBox.allNotifications.length,

Просмотреть файл

@ -187,21 +187,23 @@ add_task(async function test_can_update_notification() {
// should have fired, so the notification should be visible. // should have fired, so the notification should be visible.
let notificationBox = gBrowser.getNotificationBox(browser); let notificationBox = gBrowser.getNotificationBox(browser);
let notification = notificationBox.currentNotification; 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( is(
message.dataset.l10nId, notification.messageText.textContent.trim(),
"refresh-blocked-redirect-label", redirectLabel,
"Should be showing the redirect message" "Should be showing the redirect message"
); );
// Next, attempt a refresh // Next, attempt a refresh
await attemptFakeRefresh(browser, false); await attemptFakeRefresh(browser, false);
message = notification.messageText.querySelector("span");
is( is(
message.dataset.l10nId, notification.messageText.textContent.trim(),
"refresh-blocked-refresh-label", refreshLabel,
"Should be showing the refresh message" "Should be showing the refresh message"
); );
} }

Просмотреть файл

@ -31,15 +31,16 @@ add_task(async function showNotification() {
verifyTelemetry("initial", 0, 0, 0, 0, 0, 0); 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", label: "Message for tab 3",
priority: box3.PRIORITY_INFO_HIGH, priority: box3.PRIORITY_INFO_HIGH,
telemetry: TELEMETRY_BASE + "testtwo", telemetry: TELEMETRY_BASE + "testtwo",
}); });
await notif3.updateComplete;
verifyTelemetry("first notification", 0, 0, 0, 0, 0, 1); verifyTelemetry("first notification", 0, 0, 0, 0, 0, 1);
let notif1 = box1.appendNotification( let notif1 = await box1.appendNotification(
"infobar-testone-value", "infobar-testone-value",
{ {
label: "Message for tab 1", 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); verifyTelemetry("second notification", 0, 0, 0, 0, 0, 1);
await BrowserTestUtils.switchTab(gBrowser, tab1); await BrowserTestUtils.switchTab(gBrowser, tab1);
@ -89,7 +91,7 @@ add_task(async function showNotification() {
notif3.dismiss(); notif3.dismiss();
verifyTelemetry("dismiss notification for box 3", 1, 1, 1, 1, 1, 1, 1); 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", "infobar-testtwo-value",
{ {
label: "Additional message for tab 1", 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); verifyTelemetry("show first filtered notification", 2, 1, 1, 1, 1, 1, 1);
notif4.buttonContainer.lastElementChild.click(); notif4.buttonContainer.lastElementChild.click();
notif4.dismiss(); notif4.dismiss();
verifyTelemetry("dismiss first filtered notification", 2, 1, 1, 1, 1, 1, 1); verifyTelemetry("dismiss first filtered notification", 2, 1, 1, 1, 1, 1, 1);
let notif5 = box1.appendNotification( let notif5 = await box1.appendNotification(
"infobar-testtwo-value", "infobar-testtwo-value",
{ {
label: "Dimissed additional message for tab 1", 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); verifyTelemetry("show second filtered notification", 2, 1, 1, 1, 1, 1, 1);
notif5.buttonContainer.lastElementChild.click(); notif5.buttonContainer.lastElementChild.click();
notif5.dismiss(); notif5.dismiss();
verifyTelemetry("dismiss second filtered notification", 2, 1, 1, 1, 2, 1, 1); verifyTelemetry("dismiss second filtered notification", 2, 1, 1, 1, 2, 1, 1);
let notif6 = box1.appendNotification( let notif6 = await box1.appendNotification(
"infobar-testtwo-value", "infobar-testtwo-value",
{ {
label: "Dimissed additional message for tab 1", 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); verifyTelemetry("show third filtered notification", 2, 1, 1, 1, 2, 1, 1);
notif6.buttonContainer.lastElementChild.click(); notif6.buttonContainer.lastElementChild.click();

Просмотреть файл

@ -32,9 +32,9 @@ function assertNotificationBoxShown(reason, browser) {
is(selectedViewName, name, `Box is shown ${reason}`); 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 notificationBox = gBrowser.getNotificationBox(browser);
let notification = notificationBox.appendNotification(value, { let notification = await notificationBox.appendNotification(value, {
label, label,
priority: notificationBox[priority], priority: notificationBox[priority],
}); });
@ -52,7 +52,7 @@ add_task(async function testNotificationInBackgroundTab() {
gBrowser.selectedTab = firstTab; gBrowser.selectedTab = firstTab;
assertNotificationBoxHidden("initial first tab"); assertNotificationBoxHidden("initial first tab");
createNotification({ await createNotification({
browser, browser,
label: "My notification body", label: "My notification body",
value: "test-notification", value: "test-notification",
@ -69,7 +69,7 @@ add_task(async function testNotificationInActiveTab() {
await BrowserTestUtils.withNewTab("about:blank", async browser => { await BrowserTestUtils.withNewTab("about:blank", async browser => {
ok(!gBrowser.readNotificationBox(browser), "No notifications for new tab"); ok(!gBrowser.readNotificationBox(browser), "No notifications for new tab");
createNotification({ await createNotification({
browser, browser,
label: "Notification!", label: "Notification!",
value: "test-notification", value: "test-notification",
@ -108,7 +108,7 @@ add_task(async function testNotificationMultipleTabs() {
assertNotificationBoxHidden("after open", browserTwo); assertNotificationBoxHidden("after open", browserTwo);
assertNotificationBoxHidden("after open", browserThree); assertNotificationBoxHidden("after open", browserThree);
createNotification({ await createNotification({
browser: browserTwo, browser: browserTwo,
label: "Test blank", label: "Test blank",
value: "blank", value: "blank",
@ -121,7 +121,7 @@ add_task(async function testNotificationMultipleTabs() {
assertNotificationBoxHidden("hidden create", browserTwo); assertNotificationBoxHidden("hidden create", browserTwo);
assertNotificationBoxHidden("other create", browserThree); assertNotificationBoxHidden("other create", browserThree);
createNotification({ await createNotification({
browser: browserThree, browser: browserThree,
label: "Test active tab", label: "Test active tab",
value: "active", value: "active",

Просмотреть файл

@ -36,6 +36,11 @@ add_task(async function test_drm_prompt_shows_for_toplevel() {
// Turn off EME and Widevine CDM. // Turn off EME and Widevine CDM.
Services.prefs.setBoolPref("media.eme.enabled", false); Services.prefs.setBoolPref("media.eme.enabled", false);
Services.prefs.setBoolPref("media.gmp-widevinecdm.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 // Have content request access to Widevine, UI should drop down to
// prompt user to enable DRM. // prompt user to enable DRM.
@ -64,7 +69,9 @@ add_task(async function test_drm_prompt_shows_for_toplevel() {
// Verify the UI prompt showed. // Verify the UI prompt showed.
let box = gBrowser.getNotificationBox(browser); let box = gBrowser.getNotificationBox(browser);
await notificationShownPromise;
let notification = box.currentNotification; let notification = box.currentNotification;
await notification.updateComplete;
ok(notification, "Notification should be visible"); ok(notification, "Notification should be visible");
is( is(
@ -152,6 +159,11 @@ add_task(async function test_drm_prompt_shows_for_cross_origin_iframe() {
// Turn off EME and Widevine CDM. // Turn off EME and Widevine CDM.
Services.prefs.setBoolPref("media.eme.enabled", false); Services.prefs.setBoolPref("media.eme.enabled", false);
Services.prefs.setBoolPref("media.gmp-widevinecdm.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 // Have content request access to Widevine, UI should drop down to
// prompt user to enable DRM. // 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. // Verify the UI prompt showed.
let box = gBrowser.getNotificationBox(browser); let box = gBrowser.getNotificationBox(browser);
await notificationShownPromise;
let notification = box.currentNotification; let notification = box.currentNotification;
await notification.updateComplete;
ok(notification, "Notification should be visible"); ok(notification, "Notification should be visible");
is( is(

Просмотреть файл

@ -54,7 +54,7 @@ add_task(async function () {
"Correct priority." "Correct priority."
); );
is( is(
notification.messageText.textContent, notification.messageText.textContent.trim(),
"The GlobalTestPlugin plugin has crashed.", "The GlobalTestPlugin plugin has crashed.",
"Correct message." "Correct message."
); );

Просмотреть файл

@ -3736,12 +3736,12 @@ BrowserGlue.prototype = {
* Show the notificationBox for a locked places database. * Show the notificationBox for a locked places database.
*/ */
_showPlacesLockedNotificationBox: _showPlacesLockedNotificationBox:
function BG__showPlacesLockedNotificationBox() { async function BG__showPlacesLockedNotificationBox() {
var win = lazy.BrowserWindowTracker.getTopWindow(); var win = lazy.BrowserWindowTracker.getTopWindow();
var buttons = [{ supportPage: "places-locked" }]; var buttons = [{ supportPage: "places-locked" }];
var notifyBox = win.gBrowser.getNotificationBox(); var notifyBox = win.gBrowser.getNotificationBox();
var notification = notifyBox.appendNotification( var notification = await notifyBox.appendNotification(
"places-locked", "places-locked",
{ {
label: { "l10n-id": "places-locked-prompt" }, label: { "l10n-id": "places-locked-prompt" },
@ -4616,7 +4616,7 @@ BrowserGlue.prototype = {
]; ];
const notifyBox = win.gBrowser.getNotificationBox(); const notifyBox = win.gBrowser.getNotificationBox();
const notification = notifyBox.appendNotification( const notification = await notifyBox.appendNotification(
"startup-restore-session-suggestion", "startup-restore-session-suggestion",
{ {
label: messageFragment, label: messageFragment,

Просмотреть файл

@ -40,16 +40,9 @@ add_task(async function () {
"info", "info",
"We expect this notification to have the type of 'info'." "We expect this notification to have the type of 'info'."
); );
is(
// Make sure the CSS is fully loaded... notification.messageImage.getAttribute("src"),
ok( "chrome://global/skin/icons/info-filled.svg",
await TestUtils.waitForCondition(
() =>
notification.ownerGlobal.getComputedStyle(
notification.messageImage,
"::after"
).backgroundImage == 'url("chrome://global/skin/icons/info-filled.svg")'
),
"We expect this notification to have an icon." "We expect this notification to have an icon."
); );

Просмотреть файл

@ -46,7 +46,7 @@ addRDMTask(
const notificationEl = box.currentNotification; const notificationEl = box.currentNotification;
ok(notificationEl, "Notification should be visible"); ok(notificationEl, "Notification should be visible");
is( is(
notificationEl.messageText.textContent, notificationEl.messageText.textContent.trim(),
"The device pixel ratio was reduced to 1 as the resulting image was too large", "The device pixel ratio was reduced to 1 as the resulting image was too large",
"The expected warning was displayed" "The expected warning was displayed"
); );

Просмотреть файл

@ -112,7 +112,7 @@ async function test_decoder_doctor_notification(
label = await document.l10n.formatValue(label.l10nId); label = await document.l10n.formatValue(label.l10nId);
} }
if (isLink) { if (isLink) {
let link = notification.messageText.querySelector("a"); let link = notification.supportLinkEls[0];
if (link) { if (link) {
// Seems to be a Windows specific quirk, but without this // Seems to be a Windows specific quirk, but without this
// mutation observer the notification.messageText.textContent // mutation observer the notification.messageText.textContent
@ -126,13 +126,13 @@ async function test_decoder_doctor_notification(
} }
} }
is( is(
notification.messageText.textContent, notification.messageText.textContent.trim(),
notificationMessage + (isLink && label ? ` ${label}` : ""), notificationMessage,
"notification message should match expectation" "notification message should match expectation"
); );
let button = notification.buttonContainer.querySelector("button"); let button = notification.buttonContainer.querySelector("button");
let link = notification.messageText.querySelector("a"); let link = notification.supportLinkEls[0];
if (!label) { if (!label) {
ok(!button, "There should not be a button"); ok(!button, "There should not be a button");
ok(!link, "There should not be a link"); ok(!link, "There should not be a link");
@ -141,7 +141,7 @@ async function test_decoder_doctor_notification(
if (isLink) { if (isLink) {
ok(!button, "There should not be a button"); 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( ok(
!link.hasAttribute("accesskey"), !link.hasAttribute("accesskey"),
"notification link should not have accesskey" "notification link should not have accesskey"

Просмотреть файл

@ -123,7 +123,7 @@ class TestSafeBrowsingNotificationBar(WindowManagerMixin, MarionetteTestCase):
message = notification_box.find_element( message = notification_box.find_element(
By.CSS_SELECTOR, "notification-message[value=blocked-badware-page]" By.CSS_SELECTOR, "notification-message[value=blocked-badware-page]"
) )
button = message.get_property("closeButton") button = message.get_property("closeButtonEl")
button.click() button.click()
Wait(self.marionette, timeout=self.marionette.timeout.page_load).until( Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(

Просмотреть файл

@ -106,6 +106,7 @@ toolkit.jar:
content/global/elements/moz-toggle.css (widgets/moz-toggle/moz-toggle.css) 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/moz-toggle.mjs (widgets/moz-toggle/moz-toggle.mjs)
content/global/elements/named-deck.js (widgets/named-deck.js) 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/notificationbox.js (widgets/notificationbox.js)
content/global/elements/panel.js (widgets/panel.js) content/global/elements/panel.js (widgets/panel.js)
content/global/elements/panel-item.css (widgets/panel-list/panel-item.css) content/global/elements/panel-item.css (widgets/panel-list/panel-item.css)

Просмотреть файл

@ -28,14 +28,14 @@ function completeAnimation(nextTest) {
setTimeout(completeAnimation, 50, nextTest); setTimeout(completeAnimation, 50, nextTest);
} }
function test() { async function test() {
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
gNotificationBox = new MozElements.NotificationBox(e => { gNotificationBox = new MozElements.NotificationBox(e => {
document.getElementById("nb").appendChild(e); document.getElementById("nb").appendChild(e);
}); });
is(gNotificationBox.allNotifications.length, 0, "There should be no initial notifications"); 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 }); { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW });
is(gNotificationBox.allNotifications.length, 1, "Notification exists while animating in"); is(gNotificationBox.allNotifications.length, 1, "Notification exists while animating in");
let notification = gNotificationBox.getNotificationWithValue("notification1"); let notification = gNotificationBox.getNotificationWithValue("notification1");
@ -46,7 +46,7 @@ function test() {
} }
// Tests that a notification that is fully animated in gets removed immediately // Tests that a notification that is fully animated in gets removed immediately
function test1() { async function test1() {
let notification = gNotificationBox.getNotificationWithValue("notification1"); let notification = gNotificationBox.getNotificationWithValue("notification1");
gNotificationBox.removeNotification(notification); gNotificationBox.removeNotification(notification);
notification = gNotificationBox.getNotificationWithValue("notification1"); notification = gNotificationBox.getNotificationWithValue("notification1");
@ -59,8 +59,8 @@ function test1() {
} }
// Tests that a notification that is animating in gets removed immediately // Tests that a notification that is animating in gets removed immediately
function test2() { async function test2() {
let notification = gNotificationBox.appendNotification("notification2", let notification = await gNotificationBox.appendNotification("notification2",
{ label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW });
gNotificationBox.removeNotification(notification); gNotificationBox.removeNotification(notification);
notification = gNotificationBox.getNotificationWithValue("notification2"); notification = gNotificationBox.getNotificationWithValue("notification2");
@ -74,10 +74,10 @@ function test2() {
} }
// Tests that a background notification goes away immediately // Tests that a background notification goes away immediately
function test3() { async function test3() {
let notification = gNotificationBox.appendNotification("notification3", let notification = await gNotificationBox.appendNotification("notification3",
{ label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); { 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 }); { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW });
is(gNotificationBox.allNotifications.length, 2, "Test 3 should show 2 notifications present"); is(gNotificationBox.allNotifications.length, 2, "Test 3 should show 2 notifications present");
gNotificationBox.removeNotification(notification); gNotificationBox.removeNotification(notification);
@ -96,10 +96,10 @@ function test3() {
} }
// Tests that a foreground notification hiding a background one goes away // Tests that a foreground notification hiding a background one goes away
function test4() { async function test4() {
let notification = gNotificationBox.appendNotification("notification5", let notification = await gNotificationBox.appendNotification("notification5",
{ label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); { 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 }); { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW });
gNotificationBox.removeNotification(notification2); gNotificationBox.removeNotification(notification2);
notification2 = gNotificationBox.getNotificationWithValue("notification6"); notification2 = gNotificationBox.getNotificationWithValue("notification6");
@ -118,10 +118,10 @@ function test4() {
} }
// Tests that removeAllNotifications gets rid of everything // Tests that removeAllNotifications gets rid of everything
function test5() { async function test5() {
let notification = gNotificationBox.appendNotification("notification7", let notification = await gNotificationBox.appendNotification("notification7",
{ label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW }); { 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 }); { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW });
gNotificationBox.removeAllNotifications(); gNotificationBox.removeAllNotifications();
notification = gNotificationBox.getNotificationWithValue("notification7"); notification = gNotificationBox.getNotificationWithValue("notification7");
@ -131,7 +131,7 @@ function test5() {
ok(!gNotificationBox.currentNotification, "Test 5 said there was still a current notification"); ok(!gNotificationBox.currentNotification, "Test 5 said there was still a current notification");
is(gNotificationBox.allNotifications.length, 0, "Test 5 should show 0 notifications present"); 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 }); { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW });
// Wait for the notificaton to finish displaying // Wait for the notificaton to finish displaying
@ -139,7 +139,7 @@ function test5() {
} }
// Tests whether removing an already removed notification doesn't break things // Tests whether removing an already removed notification doesn't break things
function test6() { async function test6() {
let notification = gNotificationBox.getNotificationWithValue("notification9"); let notification = gNotificationBox.getNotificationWithValue("notification9");
ok(notification, "Test 6 should have an initial notification"); ok(notification, "Test 6 should have an initial notification");
gNotificationBox.removeNotification(notification); gNotificationBox.removeNotification(notification);
@ -147,7 +147,7 @@ function test6() {
ok(!gNotificationBox.currentNotification, "Test 6 shouldn't be any current notification"); ok(!gNotificationBox.currentNotification, "Test 6 shouldn't be any current notification");
is(gNotificationBox.allNotifications.length, 0, "Test 6 allNotifications.length should be 0"); 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 }); { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW });
is(notification, gNotificationBox.currentNotification, "Test 6 should have made the current notification"); is(notification, gNotificationBox.currentNotification, "Test 6 should have made the current notification");
gNotificationBox.removeNotification(notification); gNotificationBox.removeNotification(notification);

Просмотреть файл

@ -20,7 +20,7 @@
var gNotificationBox; var gNotificationBox;
// Tests that a notification that is added in an hidden box didn't throw the animation // Tests that a notification that is added in an hidden box didn't throw the animation
function test() { async function test() {
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
gNotificationBox = new MozElements.NotificationBox(e => { gNotificationBox = new MozElements.NotificationBox(e => {
document.getElementById("nb").appendChild(e); document.getElementById("nb").appendChild(e);
@ -28,7 +28,7 @@ function test() {
is(gNotificationBox.allNotifications.length, 0, "There should be no initial notifications"); 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 }); { label: "Test notification", priority: gNotificationBox.PRIORITY_INFO_LOW });
is(gNotificationBox.allNotifications.length, 1, "Notification exists"); is(gNotificationBox.allNotifications.length, 1, "Notification exists");

Просмотреть файл

@ -138,18 +138,15 @@ function testtag_notification_eventCallback(expectedEvents, ntf, testName)
var tests = var tests =
[ [
{ {
test(nb, ntf) { async test(nb, ntf) {
ntf = nb.appendNotification("mutable", { ntf = await nb.appendNotification("mutable", {
label: "Original", label: "Original",
priority: nb.PRIORITY_INFO_LOW, priority: nb.PRIORITY_INFO_LOW,
}, testtag_notificationbox_buttons); }, testtag_notificationbox_buttons);
ntf.label = "Changed string"; ntf.label = "Changed string";
SimpleTest.is(ntf.messageText.textContent, "Changed string", "set notification label with string"); await ntf.updateComplete;
SimpleTest.is(ntf.messageText.textContent.trim(), "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");
return ntf; return ntf;
}, },
result(nb, ntf) { result(nb, ntf) {
@ -162,8 +159,8 @@ var tests =
label attribute set correctly. label attribute set correctly.
*/ */
{ {
test(nb, ntf) { async test(nb, ntf) {
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_LOW, priority: nb.PRIORITY_INFO_LOW,
@ -183,8 +180,8 @@ var tests =
Ensures that buttons created with the "l10n-id" parameter have Ensures that buttons created with the "l10n-id" parameter have
their "l10n-id" assigned correctly. their "l10n-id" assigned correctly.
*/ */
test(nb, ntf) { async test(nb, ntf) {
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_LOW, priority: nb.PRIORITY_INFO_LOW,
@ -200,9 +197,9 @@ var tests =
} }
}, },
{ {
test(nb, ntf) { async test(nb, ntf) {
// append a new notification // append a new notification
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_LOW, 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 // append a new notification, but now with an event callback
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_LOW, priority: nb.PRIORITY_INFO_LOW,
@ -265,8 +262,8 @@ var tests =
} }
}, },
{ {
test(nb, ntf) { async test(nb, ntf) {
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_MEDIUM, priority: nb.PRIORITY_INFO_MEDIUM,
@ -299,8 +296,8 @@ var tests =
} }
}, },
{ {
test(nb, ntf) { async test(nb, ntf) {
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_WARNING_LOW, priority: nb.PRIORITY_WARNING_LOW,
@ -321,13 +318,13 @@ var tests =
}, },
{ {
repeat: true, repeat: true,
test(nb, arr) { async test(nb, arr) {
var idx = arr[0]; var idx = arr[0];
var ntf = arr[1]; var ntf = arr[1];
switch (idx) { switch (idx) {
case 1: case 1:
// append a new notification // append a new notification
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_LOW, priority: nb.PRIORITY_INFO_LOW,
@ -373,9 +370,9 @@ var tests =
} }
}, },
{ {
test(nb, ntf) { async test(nb, ntf) {
// append another notification // append another notification
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_MEDIUM, priority: nb.PRIORITY_INFO_MEDIUM,
@ -402,8 +399,8 @@ var tests =
} }
}, },
{ {
test(nb, ntf) { async test(nb, ntf) {
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_HIGH, priority: nb.PRIORITY_INFO_HIGH,
@ -419,8 +416,8 @@ var tests =
} }
}, },
{ {
test(nb, ntf) { async test(nb, ntf) {
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_LOW, priority: nb.PRIORITY_INFO_LOW,
@ -437,7 +434,7 @@ var tests =
SimpleTest.is(button.localName, "button", "button is a button"); SimpleTest.is(button.localName, "button", "button is a button");
SimpleTest.ok(!button.href, "button href is not set"); 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.localName, "label", "link is a label");
SimpleTest.is(link.href, "about:mozilla", "link href is correct"); 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 // append a new notification
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_LOW, priority: nb.PRIORITY_INFO_LOW,
@ -473,20 +470,21 @@ var tests =
} }
}, },
{ {
test(nb, ntf) { async test(nb, ntf) {
ntf = nb.appendNotification("note", { ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_LOW, priority: nb.PRIORITY_INFO_LOW,
eventCallback: notification_eventCallback, eventCallback: notification_eventCallback,
}, testtag_notificationbox_supportpage); }, testtag_notificationbox_supportpage);
await ntf.updateComplete;
SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append support page notification"); SimpleTest.is(ntf && ntf.localName == NOTIFICATION_LOCAL_NAME, true, "append support page notification");
return ntf; return ntf;
}, },
result(nb, ntf) { result(nb, ntf) {
testtag_notificationbox_State(nb, "append link with callback", ntf, 1); 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.localName, "a", "link 1 is an anchor");
SimpleTest.is(link.dataset.l10nId, "moz-support-link-text", "link 1 Fluent ID is set"); 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"); 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 // 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); 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); testtag_notificationbox_buttons);
nb.appendNotification("2", { label: "Two", priority: nb.PRIORITY_INFO_LOW }); await nb.appendNotification("2", { label: "Two", priority: nb.PRIORITY_INFO_LOW });
nb.appendNotification("8", { label: "Eight", priority: nb.PRIORITY_CRITICAL_LOW }); await nb.appendNotification("8", { label: "Eight", priority: nb.PRIORITY_CRITICAL_LOW });
nb.appendNotification("5", { label: "Five", priority: nb.PRIORITY_WARNING_LOW }); await nb.appendNotification("5", { label: "Five", priority: nb.PRIORITY_WARNING_LOW });
nb.appendNotification("6", { label: "Six", priority: nb.PRIORITY_WARNING_HIGH }); await nb.appendNotification("6", { label: "Six", priority: nb.PRIORITY_WARNING_HIGH });
nb.appendNotification("1", { label: "One", priority: nb.PRIORITY_INFO_LOW }); await nb.appendNotification("1", { label: "One", priority: nb.PRIORITY_INFO_LOW });
nb.appendNotification("9", { label: "Nine", priority: nb.PRIORITY_CRITICAL_MEDIUM }); await nb.appendNotification("9", { label: "Nine", priority: nb.PRIORITY_CRITICAL_MEDIUM });
let ntf = nb.appendNotification("10", { label: "Ten", priority: nb.PRIORITY_CRITICAL_HIGH }); let ntf = await nb.appendNotification("10", { label: "Ten", priority: nb.PRIORITY_CRITICAL_HIGH });
nb.appendNotification("3", { label: "Three", priority: nb.PRIORITY_INFO_MEDIUM }); await nb.appendNotification("3", { label: "Three", priority: nb.PRIORITY_INFO_MEDIUM });
return ntf; return ntf;
}, },
result(nb, ntf) { result(nb, ntf) {
@ -558,16 +556,16 @@ var tests =
} }
}, },
{ {
test(nb, ntf) { async test(nb, ntf) {
var exh = false; var exh = false;
try { try {
nb.appendNotification("no", { label: "no", priority: -1 }); await nb.appendNotification("no", { label: "no", priority: -1 });
} catch (ex) { exh = true; } } catch (ex) { exh = true; }
SimpleTest.is(exh, true, "appendNotification priority too low"); SimpleTest.is(exh, true, "appendNotification priority too low");
exh = false; exh = false;
try { try {
nb.appendNotification("no", { label: "no", priority: 11 }); await nb.appendNotification("no", { label: "no", priority: 11 });
} catch (ex) { exh = true; } } catch (ex) { exh = true; }
SimpleTest.is(exh, true, "appendNotification priority too high"); SimpleTest.is(exh, true, "appendNotification priority too high");
@ -579,8 +577,8 @@ var tests =
var appendPriorityTests = [ var appendPriorityTests = [
{ {
test(nb, priority) { async test(nb, priority) {
let ntf = nb.appendNotification("note", { let ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority, priority,
@ -598,9 +596,9 @@ var appendPriorityTests = [
nb.removeCurrentNotification(); nb.removeCurrentNotification();
return priority; return priority;
}, },
result(nb, priority) { async result(nb, priority) {
if (priority == nb.PRIORITY_CRITICAL_HIGH) { if (priority == nb.PRIORITY_CRITICAL_HIGH) {
let ntf = nb.appendNotification("note", { let ntf = await nb.appendNotification("note", {
label: "Notification", label: "Notification",
image: "happy.png", image: "happy.png",
priority: nb.PRIORITY_INFO_LOW, 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) 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.getAttribute("value"), value, testid + " notification value");
is(ntf.priority, priority, testid + " notification priority"); 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", critical: "chrome://global/skin/icons/error.svg",
}; };
let icon = icons[type]; 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) function checkPopupTest(nb, ntf)
@ -697,7 +695,7 @@ function checkPopupClosed()
* function as its arg. The result function may also return a value which * 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. * 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]) if (idx >= 0 && "result" in tests[idx])
arg = tests[idx].result(element, arg); arg = tests[idx].result(element, arg);
@ -707,7 +705,7 @@ function runTimedTests(tests, idx, element, arg)
idx++; idx++;
if (idx < tests.length) { 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); setTimeout(runTimedTestsWait, 50, tests, idx, element, result);
} }
} }

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -13,10 +13,6 @@
--close-icon-size: 28px; --close-icon-size: 28px;
} }
:host([message-bar-type=infobar]) {
--close-icon-size: 28px;
}
:host { :host {
--message-bar-background-color: var(--in-content-box-info-background); --message-bar-background-color: var(--in-content-box-info-background);
--message-bar-text-color: var(--in-content-text-color); --message-bar-text-color: var(--in-content-text-color);
@ -28,7 +24,7 @@
--message-bar-icon-url: var(--warn-icon-url); --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); --message-bar-icon-url: var(--success-icon-url);
} }
@ -125,26 +121,23 @@
min-height: auto; min-height: auto;
width: var(--close-icon-size); width: var(--close-icon-size);
height: var(--close-icon-size); height: var(--close-icon-size);
margin: 0;
padding: 0; padding: 0;
flex-shrink: 0; flex-shrink: 0;
margin: 4px 8px;
background-size: 12px;
} }
@media (prefers-contrast) { @media (prefers-contrast) {
:host { :host {
border-color: CanvasText; border-color: CanvasText;
} }
.container.infobar::before {
background-color: CanvasText;
}
} }
@media not (prefers-contrast) { @media not (prefers-contrast) {
/* MessageBar colors by message type */ /* MessageBar colors by message type */
/* Colors from: https://design.firefox.com/photon/components/message-bars.html#type-specific-style */ /* 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 */ /* Ensure colors within the bar are adjusted and controls are readable */
color-scheme: light; color-scheme: light;
@ -158,7 +151,7 @@
--close-fill-color: var(--message-bar-text-color); --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 */ /* Ensure colors within the bar are adjusted and controls are readable */
color-scheme: light; color-scheme: light;
@ -170,7 +163,7 @@
--in-content-button-background-active: var(--green-80); --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-background-color: var(--red-60);
--message-bar-text-color: #ffffff; --message-bar-text-color: #ffffff;
@ -191,15 +184,6 @@
color: rgb(226,40,80); 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 { .close {
fill: var(--close-fill-color); fill: var(--close-fill-color);
} }
@ -207,10 +191,6 @@
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
/* Don't set the background in prefers-contrast mode or macOS can end up /* Don't set the background in prefers-contrast mode or macOS can end up
* with black on black text. */ * with black on black text. */
.container.infobar {
background: rgb(66,65,77);
}
:host([type=info]) .icon { :host([type=info]) .icon {
color: rgb(128,235,255); 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 { strong {
font-weight: 600; font-weight: 600;
} }
@ -308,20 +213,6 @@ strong {
cursor: pointer; 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 { @keyframes spin {
from { transform: rotate(0); } from { transform: rotate(0); }
to { transform: rotate(360deg); } to { transform: rotate(360deg); }

Просмотреть файл

@ -177,26 +177,27 @@
:host([type=warning]) { :host([type=warning]) {
--message-bar-background-color: var(--color-background-warning); --message-bar-background-color: var(--color-background-warning);
.icon {
--message-bar-icon-color: var(--icon-color-warning);
}
} }
:host([type=success]) { :host([type=success]) {
--message-bar-background-color: var(--color-background-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); --message-bar-background-color: var(--color-background-critical);
}
:host([type=success]) .icon { .icon {
--message-bar-icon-color: var(--icon-color-success); --message-bar-icon-color: var(--icon-color-critical);
} }
:host([type=warning]) .icon {
--message-bar-icon-color: var(--icon-color-warning);
}
:host([type=error]) .icon {
--message-bar-icon-color: var(--icon-color-critical);
} }
.close { .close {

Просмотреть файл

@ -22,6 +22,10 @@ const messageTypeToIconData = {
iconSrc: "chrome://global/skin/icons/error.svg", iconSrc: "chrome://global/skin/icons/error.svg",
l10nId: "moz-message-bar-icon-error", 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} heading - The heading of the message.
* @property {string} message - The message text. * @property {string} message - The message text.
* @property {boolean} dismissable - Whether or not the element is dismissable. * @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 * @fires message-bar:close
* Custom event indicating that message bar was closed. * Custom event indicating that message bar was closed.
* @fires message-bar:user-dismissed * @fires message-bar:user-dismissed
@ -44,6 +50,7 @@ export default class MozMessageBar extends MozLitElement {
actionsSlotEl: "slot[name=actions]", actionsSlotEl: "slot[name=actions]",
actionsEl: ".actions", actionsEl: ".actions",
closeButtonEl: "button.close", closeButtonEl: "button.close",
supportLinkSlotEl: "slot[name=support-link]",
}; };
static properties = { static properties = {
@ -51,6 +58,8 @@ export default class MozMessageBar extends MozLitElement {
heading: { type: String }, heading: { type: String },
message: { type: String }, message: { type: String },
dismissable: { type: Boolean }, dismissable: { type: Boolean },
messageL10nId: { type: String },
messageL10nArgs: { type: String },
}; };
constructor() { constructor() {
@ -75,6 +84,10 @@ export default class MozMessageBar extends MozLitElement {
this.dispatchEvent(new CustomEvent("message-bar:close")); this.dispatchEvent(new CustomEvent("message-bar:close"));
} }
get supportLinkEls() {
return this.supportLinkSlotEl.assignedElements();
}
iconTemplate() { iconTemplate() {
let iconData = messageTypeToIconData[this.type]; let iconData = messageTypeToIconData[this.type];
if (iconData) { if (iconData) {
@ -126,7 +139,15 @@ export default class MozMessageBar extends MozLitElement {
<div class="text-content"> <div class="text-content">
${this.headingTemplate()} ${this.headingTemplate()}
<div> <div>
<span class="message">${ifDefined(this.message)}</span> <span
class="message"
data-l10n-id=${ifDefined(this.messageL10nId)}
data-l10n-args=${ifDefined(
JSON.stringify(this.messageL10nArgs)
)}
>
${this.message}
</span>
<span class="link"> <span class="link">
<slot name="support-link"></slot> <slot name="support-link"></slot>
</span> </span>

Просмотреть файл

@ -144,7 +144,7 @@
* *
* @return The <notification> element that is shown. * @return The <notification> element that is shown.
*/ */
appendNotification(aType, aNotification, aButtons) { async appendNotification(aType, aNotification, aButtons) {
if ( if (
aNotification.priority < this.PRIORITY_SYSTEM || aNotification.priority < this.PRIORITY_SYSTEM ||
aNotification.priority > this.PRIORITY_CRITICAL_HIGH aNotification.priority > this.PRIORITY_CRITICAL_HIGH
@ -157,12 +157,19 @@
MozXULElement.insertFTLIfNeeded("toolkit/global/notification.ftl"); MozXULElement.insertFTLIfNeeded("toolkit/global/notification.ftl");
// Create the Custom Element and connect it to the document immediately. // Create the Custom Element and connect it to the document immediately.
var newitem; let newitem;
if (!aNotification.notificationIs) { if (!aNotification.notificationIs) {
if (!customElements.get("notification-message")) { if (!customElements.get("notification-message")) {
// There's some weird timing stuff when this element is created at // 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. // 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 = document.createElement("notification-message");
newitem.setAttribute("message-bar-type", "infobar"); newitem.setAttribute("message-bar-type", "infobar");
@ -182,8 +189,10 @@
this.stack.append(newitem); this.stack.append(newitem);
} }
// Custom notification classes may not have the messageText property. if (newitem.localName === "notification-message" && aNotification.label) {
if (newitem.messageText) { 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: // Can't use instanceof in case this was created from a different document:
if ( if (
aNotification.label && aNotification.label &&
@ -241,6 +250,10 @@
newitem.style.top = "100%"; newitem.style.top = "100%";
newitem.style.marginTop = "-15px"; newitem.style.marginTop = "-15px";
newitem.style.opacity = "0"; 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); this._showNotification(newitem, true);
// Fire event for accessibility APIs // Fire event for accessibility APIs
@ -607,53 +620,56 @@
customElements.define("notification", MozElements.Notification); customElements.define("notification", MozElements.Notification);
function createNotificationMessageElement() { async function createNotificationMessageElement() {
// Get a reference to MessageBarElement from a created element so the import await window.ensureCustomElements("moz-message-bar");
// gets handled automatically if needed. let MozMessageBar = customElements.get("moz-message-bar");
class NotificationMessage extends document.createElement("message-bar") class NotificationMessage extends MozMessageBar {
.constructor { static queries = {
...MozMessageBar.queries,
messageText: ".message",
messageImage: ".icon",
};
constructor() { constructor() {
super(); super();
this.persistence = 0; this.persistence = 0;
this.priority = 0; this.priority = 0;
this.timeout = 0; this.timeout = 0;
this.telemetry = null; this.telemetry = null;
this.dismissable = true;
this._shown = false; this._shown = false;
this.addEventListener("click", this);
this.addEventListener("command", this);
} }
connectedCallback() { connectedCallback() {
this.toggleAttribute("dismissable", true); super.connectedCallback();
this.closeButton.classList.add("notification-close"); this.#setStyles();
this.container = this.shadowRoot.querySelector(".container"); this.classList.add("infobar");
this.container.classList.add("infobar");
this.setAlertRole(); this.setAlertRole();
let messageContent = this.shadowRoot.querySelector(".content");
messageContent.classList.add("notification-content");
// Remove the <slot>, 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 = document.createElement("span");
this.buttonContainer.classList.add("notification-button-container"); this.buttonContainer.classList.add("notification-button-container");
this.buttonContainer.setAttribute("slot", "actions");
this.messageImage = this.shadowRoot.querySelector(".icon"); this.appendChild(this.buttonContainer);
messageContent.append(this.messageText, this.buttonContainer);
this.shadowRoot.addEventListener("click", this);
this.shadowRoot.addEventListener("command", this);
} }
disconnectedCallback() { disconnectedCallback() {
super.disconnectedCallback();
if (this.eventCallback) { if (this.eventCallback) {
this.eventCallback("disconnected"); 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) { _doTelemetry(type) {
if ( if (
this.telemetry && this.telemetry &&
@ -686,10 +702,10 @@
setAlertRole() { setAlertRole() {
// Wait a little for this to render before setting the role for more // Wait a little for this to render before setting the role for more
// consistent alerts to screen readers. // consistent alerts to screen readers.
this.container.removeAttribute("role"); this.removeAttribute("role");
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
this.container.setAttribute("role", "alert"); this.setAttribute("role", "alert");
}); });
}); });
} }
@ -736,18 +752,10 @@
*/ */
set label(value) { set label(value) {
if (value && typeof value == "object" && "l10n-id" in value) { if (value && typeof value == "object" && "l10n-id" in value) {
const message = document.createElement("span"); this.messageL10nId = value["l10n-id"];
document.l10n.setAttributes( this.messageL10nArgs = value["l10n-args"];
message,
value["l10n-id"],
value["l10n-args"]
);
while (this.messageText.firstChild) {
this.messageText.firstChild.remove();
}
this.messageText.appendChild(message);
} else { } else {
this.messageText.textContent = value; this.message = value;
} }
this.setAlertRole(); this.setAlertRole();
} }
@ -777,7 +785,11 @@
"button", "button",
button.is ? { is: button.is } : {} button.is ? { is: button.is } : {}
); );
buttonElem.classList.add("notification-button", "small-button"); buttonElem.classList.add(
"notification-button",
"small-button",
"footer-button"
);
if (button.primary) { if (button.primary) {
buttonElem.classList.add("primary"); buttonElem.classList.add("primary");
@ -794,7 +806,8 @@
} }
if (link) { if (link) {
this.messageText.append(new Text(" "), buttonElem); buttonElem.setAttribute("slot", "support-link");
this.appendChild(buttonElem);
} else { } else {
this.buttonContainer.appendChild(buttonElem); this.buttonContainer.appendChild(buttonElem);
} }
@ -811,6 +824,8 @@
super.dismiss(); super.dismiss();
} }
} }
customElements.define("notification-message", NotificationMessage); if (!customElements.get("notification-message")) {
customElements.define("notification-message", NotificationMessage);
}
} }
} }

Просмотреть файл

@ -93,6 +93,7 @@
/** Size **/ /** Size **/
--size-item-small: 16px; --size-item-small: 16px;
--size-item-medium: 28px;
--size-item-large: 32px; --size-item-large: 32px;
/** Spacing **/ /** Spacing **/

Просмотреть файл

@ -225,12 +225,19 @@ button.text-link .button-text {
color: var(--button-color, inherit); color: var(--button-color, inherit);
background-color: var(--button-bgcolor, color-mix(in srgb, currentColor 13%, transparent)); background-color: var(--button-bgcolor, color-mix(in srgb, currentColor 13%, transparent));
padding: .45em 1em; padding: .45em 1em;
min-height: 32px; min-height: var(--size-item-large);
font-weight: 600; font-weight: 600;
min-width: 0; min-width: 0;
margin-inline: 8px 0; margin-inline: 8px 0;
margin-bottom: 0; margin-bottom: 0;
&.small-button {
margin: 0;
min-height: var(--size-item-medium);
padding: .6em 1em;
font-size: var(--font-size-small);
}
&[disabled] { &[disabled] {
opacity: 0.4; opacity: 0.4;
} }
@ -247,7 +254,8 @@ button.text-link .button-text {
&:hover:active { &:hover:active {
background-color: var(--button-active-bgcolor, color-mix(in srgb, currentColor 30%, transparent)); background-color: var(--button-active-bgcolor, color-mix(in srgb, currentColor 30%, transparent));
} }
&[default] { &[default],
&.primary {
color: var(--button-primary-color); color: var(--button-primary-color);
background-color: var(--button-primary-bgcolor); background-color: var(--button-primary-bgcolor);