Bug 1282768 - Part 2 - Move the secondary actions of doorhanger notifications to their own menu button. r=paolo

MozReview-Commit-ID: 2cnufF7wZJG

--HG--
extra : rebase_source : 6bb9c9f8bd52bd8e6f54cbc2b0ef2760fee25c3a
This commit is contained in:
Panos Astithas 2016-11-20 18:40:22 +01:00
Родитель 9ba5adffc9
Коммит 64baf2d729
18 изменённых файлов: 225 добавлений и 193 удалений

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

@ -47,6 +47,50 @@ var tests = [
ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered"); ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
} }
}, },
{ id: "Test#2b",
run: function () {
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.secondaryActions.push({
label: "Extra Secondary Action",
accessKey: "E",
callback: () => this.extraSecondaryActionClicked = true,
});
showNotification(this.notifyObj);
},
onShown: function (popup) {
checkPopup(popup, this.notifyObj);
triggerSecondaryCommand(popup, 1);
},
onHidden: function (popup) {
ok(this.extraSecondaryActionClicked, "extra secondary action was clicked");
ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered");
ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
}
},
{ id: "Test#2c",
run: function () {
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.secondaryActions.push({
label: "Extra Secondary Action",
accessKey: "E",
callback: () => ok(false, "unexpected callback invocation"),
}, {
label: "Other Extra Secondary Action",
accessKey: "O",
callback: () => this.extraSecondaryActionClicked = true,
});
showNotification(this.notifyObj);
},
onShown: function (popup) {
checkPopup(popup, this.notifyObj);
triggerSecondaryCommand(popup, 2);
},
onHidden: function (popup) {
ok(this.extraSecondaryActionClicked, "extra secondary action was clicked");
ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback wasn't triggered");
ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
}
},
{ id: "Test#3", { id: "Test#3",
run: function() { run: function() {
this.notifyObj = new BasicNotification(this.id); this.notifyObj = new BasicNotification(this.id);

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

@ -187,22 +187,6 @@ var tests = [
goNext(); goNext();
} }
}, },
// Test notification "Not Now" menu item
{ id: "Test#8",
run: function() {
this.notifyObj = new BasicNotification(this.id);
this.notification = showNotification(this.notifyObj);
},
onShown: function(popup) {
checkPopup(popup, this.notifyObj);
triggerSecondaryCommand(popup, 1);
},
onHidden: function(popup) {
ok(this.notifyObj.dismissalCallbackTriggered, "dismissal callback triggered");
this.notification.remove();
ok(this.notifyObj.removedCallbackTriggered, "removed callback triggered");
}
},
// Test notification close button // Test notification close button
{ id: "Test#9", { id: "Test#9",
run: function() { run: function() {

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

@ -160,23 +160,6 @@ var tests = [
goNext(); goNext();
} }
}, },
// the hideNotNow option
{ id: "Test#7",
run: function() {
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.options.hideNotNow = true;
this.notifyObj.mainAction.dismiss = true;
this.notification = showNotification(this.notifyObj);
},
onShown: function(popup) {
// checkPopup verifies that the Not Now item is hidden, and that no separator is added.
checkPopup(popup, this.notifyObj);
triggerMainCommand(popup);
},
onHidden: function(popup) {
this.notification.remove();
}
},
// the main action callback can keep the notification. // the main action callback can keep the notification.
{ id: "Test#8", { id: "Test#8",
run: function() { run: function() {

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

@ -101,7 +101,7 @@ var tests = [
ok(false, "Should have removed the notification after navigation"); ok(false, "Should have removed the notification after navigation");
// Properly dismiss and cleanup in case the unthinkable happens. // Properly dismiss and cleanup in case the unthinkable happens.
this.complete = true; this.complete = true;
triggerSecondaryCommand(popup, 1); triggerSecondaryCommand(popup, 0);
}, },
onHidden: function(popup) { onHidden: function(popup) {
ok(!this.complete, "Should have hidden the notification after navigation"); ok(!this.complete, "Should have hidden the notification after navigation");
@ -131,9 +131,9 @@ var tests = [
let browser = gBrowser.selectedBrowser; let browser = gBrowser.selectedBrowser;
yield BrowserTestUtils.synthesizeMouseAtCenter("body", {}, browser) yield BrowserTestUtils.synthesizeMouseAtCenter("body", {}, browser)
// Notification should be hidden after dismissal via Not Now. // Notification should be hidden after dismissal via Don't Allow.
this.complete = true; this.complete = true;
triggerSecondaryCommand(popup, 1); triggerSecondaryCommand(popup, 0);
}, },
onHidden: function(popup) { onHidden: function(popup) {
ok(this.complete, "Should have hidden the notification after clicking Not Now"); ok(this.complete, "Should have hidden the notification after clicking Not Now");

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

@ -213,25 +213,24 @@ function checkPopup(popup, notifyObj) {
is(notification.getAttribute("buttonaccesskey"), is(notification.getAttribute("buttonaccesskey"),
notifyObj.mainAction.accessKey, "main action accesskey matches"); notifyObj.mainAction.accessKey, "main action accesskey matches");
} }
let actualSecondaryActions = if (notifyObj.secondaryActions && notifyObj.secondaryActions.length > 0) {
let secondaryAction = notifyObj.secondaryActions[0];
is(notification.getAttribute("secondarybuttonlabel"), secondaryAction.label,
"secondary action label matches");
is(notification.getAttribute("secondarybuttonaccesskey"),
secondaryAction.accessKey, "secondary action accesskey matches");
}
// Additional secondary actions appear as menu items.
let actualExtraSecondaryActions =
Array.filter(notification.childNodes, child => child.nodeName == "menuitem"); Array.filter(notification.childNodes, child => child.nodeName == "menuitem");
let secondaryActions = notifyObj.secondaryActions || []; let extraSecondaryActions = notifyObj.secondaryActions ? notifyObj.secondaryActions.slice(1) : [];
let actualSecondaryActionsCount = actualSecondaryActions.length; is(actualExtraSecondaryActions.length, extraSecondaryActions.length,
if (notifyObj.options.hideNotNow) { "number of extra secondary actions matches");
is(notification.getAttribute("hidenotnow"), "true", "'Not Now' item hidden"); extraSecondaryActions.forEach(function(a, i) {
if (secondaryActions.length) is(actualExtraSecondaryActions[i].getAttribute("label"), a.label,
is(notification.lastChild.tagName, "menuitem", "no menuseparator"); "label for extra secondary action " + i + " matches");
} is(actualExtraSecondaryActions[i].getAttribute("accesskey"), a.accessKey,
else if (secondaryActions.length) { "accessKey for extra secondary action " + i + " matches");
is(notification.lastChild.tagName, "menuseparator", "menuseparator exists");
}
is(actualSecondaryActionsCount, secondaryActions.length,
actualSecondaryActions.length + " secondary actions");
secondaryActions.forEach(function(a, i) {
is(actualSecondaryActions[i].getAttribute("label"), a.label,
"label for secondary action " + i + " matches");
is(actualSecondaryActions[i].getAttribute("accesskey"), a.accessKey,
"accessKey for secondary action " + i + " matches");
}); });
} }
@ -272,13 +271,20 @@ function triggerSecondaryCommand(popup, index) {
let notification = notifications[0]; let notification = notifications[0];
info("Triggering secondary command for notification " + notification.id); info("Triggering secondary command for notification " + notification.id);
notification.button.nextSibling.nextSibling.focus(); if (index == 0) {
EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {});
return;
}
// Extra secondary actions appear in a menu.
notification.secondaryButton.nextSibling.nextSibling.focus();
popup.addEventListener("popupshown", function handle() { popup.addEventListener("popupshown", function handle() {
popup.removeEventListener("popupshown", handle, false); popup.removeEventListener("popupshown", handle, false);
info("Command popup open for notification " + notification.id); info("Command popup open for notification " + notification.id);
// Press down until the desired command is selected // Press down until the desired command is selected. Decrease index by one
for (let i = 0; i <= index; i++) { // since the secondary action was handled above.
for (let i = 0; i <= index - 1; i++) {
EventUtils.synthesizeKey("VK_DOWN", {}); EventUtils.synthesizeKey("VK_DOWN", {});
} }
// Activate // Activate

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

@ -305,13 +305,19 @@ const kActionNever = 3;
function activateSecondaryAction(aAction) { function activateSecondaryAction(aAction) {
let notification = PopupNotifications.panel.firstChild; let notification = PopupNotifications.panel.firstChild;
notification.button.nextSibling.nextSibling.focus();
if (aAction == kActionAlways) {
notification.secondaryButton.click();
return;
}
notification.secondaryButton.nextSibling.nextSibling.focus();
let popup = notification.menupopup; let popup = notification.menupopup;
popup.addEventListener("popupshown", function() { popup.addEventListener("popupshown", function() {
popup.removeEventListener("popupshown", arguments.callee, false); popup.removeEventListener("popupshown", arguments.callee, false);
// Press 'down' as many time as needed to select the requested action. // Press 'down' as many time as needed to select the requested action.
while (aAction--) while (--aAction)
EventUtils.synthesizeKey("VK_DOWN", {}); EventUtils.synthesizeKey("VK_DOWN", {});
// Activate // Activate

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

@ -2856,7 +2856,6 @@ var E10SAccessibilityCheck = {
learnMoreURL: Services.urlFormatter.formatURLPref("app.support.e10sAccessibilityUrl"), learnMoreURL: Services.urlFormatter.formatURLPref("app.support.e10sAccessibilityUrl"),
persistent: true, persistent: true,
persistWhileVisible: true, persistWhileVisible: true,
hideNotNow: true,
}; };
notification = notification =

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

@ -76,7 +76,6 @@ PresentationPermissionPrompt.prototype = {
}, },
get popupOptions() { get popupOptions() {
return { return {
hideNotNow: true,
removeOnDismissal: true, removeOnDismissal: true,
popupIconURL: kNotificationPopupIcon, // Icon shown on prompt content popupIconURL: kNotificationPopupIcon, // Icon shown on prompt content
eventCallback: (aTopic, aNewBrowser) => { eventCallback: (aTopic, aNewBrowser) => {
@ -162,7 +161,7 @@ PresentationPermissionPrompt.prototype = {
this._isResponded = true; this._isResponded = true;
this.request.cancel(Cr.NS_ERROR_NOT_AVAILABLE); this.request.cancel(Cr.NS_ERROR_NOT_AVAILABLE);
}, },
dismiss: true, // For hideNotNow. dismiss: true,
}]; }];
}, },
// PRIVATE APIs // PRIVATE APIs

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

@ -63,21 +63,18 @@ function clickMainAction() {
} }
/** /**
* For an opened PopupNotification, clicks on a secondary action, * For an opened PopupNotification, clicks on the secondary action,
* and waits for the panel to fully close. * and waits for the panel to fully close.
* *
* @param {int} index
* The 0-indexed index of the secondary menuitem to choose.
* @return {Promise} * @return {Promise}
* Resolves once the panel has fired the "popuphidden" * Resolves once the panel has fired the "popuphidden"
* event. * event.
*/ */
function clickSecondaryAction(index) { function clickSecondaryAction() {
let removePromise = let removePromise =
BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden"); BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden");
let popupNotification = getPopupNotificationNode(); let popupNotification = getPopupNotificationNode();
let menuitems = popupNotification.children; popupNotification.secondaryButton.click();
menuitems[index].click();
return removePromise; return removePromise;
} }
@ -274,7 +271,7 @@ add_task(function* test_with_permission_key() {
// First test denying the permission request. // First test denying the permission request.
Assert.equal(notification.secondaryActions.length, 1, Assert.equal(notification.secondaryActions.length, 1,
"There should only be 1 secondary action"); "There should only be 1 secondary action");
yield clickSecondaryAction(0); yield clickSecondaryAction();
curPerm = Services.perms.testPermissionFromPrincipal(principal, curPerm = Services.perms.testPermissionFromPrincipal(principal,
kTestPermissionKey); kTestPermissionKey);
Assert.equal(curPerm, Ci.nsIPermissionManager.DENY_ACTION, Assert.equal(curPerm, Ci.nsIPermissionManager.DENY_ACTION,
@ -429,7 +426,7 @@ add_task(function* test_no_request() {
// First test denying the permission request. // First test denying the permission request.
Assert.equal(notification.secondaryActions.length, 1, Assert.equal(notification.secondaryActions.length, 1,
"There should only be 1 secondary action"); "There should only be 1 secondary action");
yield clickSecondaryAction(0); yield clickSecondaryAction();
Assert.ok(denied, "The secondaryAction callback should have fired"); Assert.ok(denied, "The secondaryAction callback should have fired");
Assert.ok(!allowed, "The mainAction callback should not have fired"); Assert.ok(!allowed, "The mainAction callback should not have fired");

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

@ -37,7 +37,7 @@ add_task(function test1() {
}); });
registerPopupEventHandler("popupshown", function () { registerPopupEventHandler("popupshown", function () {
ok(true, "prompt shown"); ok(true, "prompt shown");
triggerSecondaryCommand(this, 0); triggerSecondaryCommand(this);
}); });
registerPopupEventHandler("popuphidden", function () { registerPopupEventHandler("popuphidden", function () {
ok(true, "prompt hidden"); ok(true, "prompt hidden");

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

@ -50,28 +50,13 @@ function triggerMainCommand(popup)
EventUtils.synthesizeMouseAtCenter(notification.button, {}); EventUtils.synthesizeMouseAtCenter(notification.button, {});
} }
function triggerSecondaryCommand(popup, index) function triggerSecondaryCommand(popup)
{ {
info("triggering secondary command, " + index); info("triggering secondary command");
let notifications = popup.childNodes; let notifications = popup.childNodes;
ok(notifications.length > 0, "at least one notification displayed"); ok(notifications.length > 0, "at least one notification displayed");
let notification = notifications[0]; let notification = notifications[0];
EventUtils.synthesizeMouseAtCenter(notification.secondaryButton, {});
notification.button.nextSibling.nextSibling.focus();
popup.addEventListener("popupshown", function () {
popup.removeEventListener("popupshown", arguments.callee, false);
// Press down until the desired command is selected
for (let i = 0; i <= index; i++)
EventUtils.synthesizeKey("VK_DOWN", {});
// Activate
EventUtils.synthesizeKey("VK_RETURN", {});
}, false);
// One down event to open the popup
EventUtils.synthesizeKey("VK_DOWN", { altKey: (navigator.platform.indexOf("Mac") == -1) });
} }
function dismissNotification(popup) function dismissNotification(popup)

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

@ -14,20 +14,18 @@ const TEST_URL = "http://mochi.test:8888/browser/dom/notification/test/browser/n
* @note modified from toolkit/components/passwordmgr/test/browser/head.js * @note modified from toolkit/components/passwordmgr/test/browser/head.js
*/ */
function clickDoorhangerButton(aButtonIndex) { function clickDoorhangerButton(aButtonIndex) {
ok(true, "Looking for action at index " + aButtonIndex);
let popup = PopupNotifications.getNotification("web-notifications"); let popup = PopupNotifications.getNotification("web-notifications");
let notifications = popup.owner.panel.childNodes; let notifications = popup.owner.panel.childNodes;
ok(notifications.length > 0, "at least one notification displayed"); ok(notifications.length > 0, "at least one notification displayed");
ok(true, notifications.length + " notification(s)"); ok(true, notifications.length + " notification(s)");
let notification = notifications[0]; let notification = notifications[0];
if (aButtonIndex == -1) { if (aButtonIndex == PROMPT_ALLOW_BUTTON) {
ok(true, "Triggering main action"); ok(true, "Triggering main action");
notification.button.doCommand(); notification.button.doCommand();
} else if (aButtonIndex <= popup.secondaryActions.length) { } else {
ok(true, "Triggering secondary action " + aButtonIndex); ok(true, "Triggering secondary action");
notification.childNodes[aButtonIndex].doCommand(); notification.secondaryButton.doCommand();
} }
} }

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

@ -118,9 +118,12 @@ function clickDoorhangerButton(aPopup, aButtonIndex) {
if (aButtonIndex == 0) { if (aButtonIndex == 0) {
ok(true, "Triggering main action"); ok(true, "Triggering main action");
notification.button.doCommand(); notification.button.doCommand();
} else if (aButtonIndex == 1) {
ok(true, "Triggering secondary action");
notification.secondaryButton.doCommand();
} else if (aButtonIndex <= aPopup.secondaryActions.length) { } else if (aButtonIndex <= aPopup.secondaryActions.length) {
ok(true, "Triggering secondary action " + aButtonIndex); ok(true, "Triggering secondary action " + aButtonIndex);
notification.childNodes[aButtonIndex].doCommand(); notification.childNodes[aButtonIndex - 1].doCommand();
} }
} }

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

@ -502,7 +502,7 @@
</xul:vbox> </xul:vbox>
<xul:toolbarbutton anonid="closebutton" <xul:toolbarbutton anonid="closebutton"
class="messageCloseButton close-icon popup-notification-closebutton tabbable" class="messageCloseButton close-icon popup-notification-closebutton tabbable"
xbl:inherits="oncommand=closebuttoncommand" xbl:inherits="oncommand=closebuttoncommand,hidden=closebuttonhidden"
tooltiptext="&closeNotification.tooltip;"/> tooltiptext="&closeNotification.tooltip;"/>
</xul:hbox> </xul:hbox>
<children includes="popupnotificationcontent"/> <children includes="popupnotificationcontent"/>
@ -513,27 +513,25 @@
<xul:description class="popup-notification-warning" xbl:inherits="hidden=warninghidden,xbl:text=warninglabel"/> <xul:description class="popup-notification-warning" xbl:inherits="hidden=warninghidden,xbl:text=warninglabel"/>
</xul:vbox> </xul:vbox>
</xul:hbox> </xul:hbox>
<xul:hbox pack="end" class="popup-notification-button-container"> <xul:hbox class="popup-notification-button-container">
<children includes="button"/> <children includes="button"/>
<xul:hbox class="popup-notification-button-wrapper"> <xul:button anonid="secondarybutton"
<xul:button anonid="button" class="popup-notification-button"
class="popup-notification-button" xbl:inherits="oncommand=secondarybuttoncommand,label=secondarybuttonlabel,accesskey=secondarybuttonaccesskey,hidden=secondarybuttonhidden"/>
default="true" <xul:toolbarseparator xbl:inherits="hidden=dropmarkerhidden"/>
xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey,disabled=mainactiondisabled"/> <xul:button type="menu"
<xul:toolbarseparator/> class="popup-notification-button popup-notification-dropmarker"
<xul:button type="menu" xbl:inherits="onpopupshown=dropmarkerpopupshown,hidden=dropmarkerhidden">
class="popup-notification-button popup-notification-dropmarker" <xul:menupopup anonid="menupopup"
xbl:inherits="onpopupshown=buttonpopupshown"> position="after_end"
<xul:menupopup anonid="menupopup" xbl:inherits="oncommand=menucommand">
position="after_end" <children/>
xbl:inherits="oncommand=menucommand"> </xul:menupopup>
<children/> </xul:button>
<xul:menuitem class="menuitem-iconic popup-notification-closeitem" <xul:button anonid="button"
label="&closeNotificationItem.label;" class="popup-notification-button"
xbl:inherits="oncommand=closeitemcommand,hidden=hidenotnow"/> default="true"
</xul:menupopup> xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey,disabled=mainactiondisabled"/>
</xul:button>
</xul:hbox>
</xul:hbox> </xul:hbox>
</content> </content>
<resources> <resources>
@ -549,6 +547,9 @@
<field name="button" readonly="true"> <field name="button" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "button"); document.getAnonymousElementByAttribute(this, "anonid", "button");
</field> </field>
<field name="secondaryButton" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "secondarybutton");
</field>
<field name="menupopup" readonly="true"> <field name="menupopup" readonly="true">
document.getAnonymousElementByAttribute(this, "anonid", "menupopup"); document.getAnonymousElementByAttribute(this, "anonid", "menupopup");
</field> </field>

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

@ -4,8 +4,6 @@
<!ENTITY closeNotification.tooltip "Close this message"> <!ENTITY closeNotification.tooltip "Close this message">
<!ENTITY closeNotificationItem.label "Not Now">
<!ENTITY checkForUpdates "Check for updates…"> <!ENTITY checkForUpdates "Check for updates…">
<!ENTITY learnMore "Learn more…"> <!ENTITY learnMore "Learn more…">

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

@ -31,7 +31,6 @@ const TELEMETRY_STAT_ACTION_LAST = 4;
const TELEMETRY_STAT_DISMISSAL_CLICK_ELSEWHERE = 5; const TELEMETRY_STAT_DISMISSAL_CLICK_ELSEWHERE = 5;
const TELEMETRY_STAT_DISMISSAL_LEAVE_PAGE = 6; const TELEMETRY_STAT_DISMISSAL_LEAVE_PAGE = 6;
const TELEMETRY_STAT_DISMISSAL_CLOSE_BUTTON = 7; const TELEMETRY_STAT_DISMISSAL_CLOSE_BUTTON = 7;
const TELEMETRY_STAT_DISMISSAL_NOT_NOW = 8;
const TELEMETRY_STAT_OPEN_SUBMENU = 10; const TELEMETRY_STAT_OPEN_SUBMENU = 10;
const TELEMETRY_STAT_LEARN_MORE = 11; const TELEMETRY_STAT_LEARN_MORE = 11;
@ -352,6 +351,8 @@ PopupNotifications.prototype = {
* removed when they would have otherwise been dismissed * removed when they would have otherwise been dismissed
* (i.e. any time the popup is closed due to user * (i.e. any time the popup is closed due to user
* interaction). * interaction).
* hideClose: Indicate that the little close button in the corner of
* the panel should be hidden.
* checkbox: An object that allows you to add a checkbox and * checkbox: An object that allows you to add a checkbox and
* control its behavior with these fields: * control its behavior with these fields:
* label: * label:
@ -372,11 +373,6 @@ PopupNotifications.prototype = {
* (optional) An object that allows you to customize * (optional) An object that allows you to customize
* the notification state when the checkbox is not checked. * the notification state when the checkbox is not checked.
* Has the same attributes as checkedState. * Has the same attributes as checkedState.
* hideNotNow: If true, indicates that the 'Not Now' menuitem should
* not be shown. If 'Not Now' is hidden, it needs to be
* replaced by another 'do nothing' item, so providing at
* least one secondary action is required; and one of the
* actions needs to have the 'dismiss' property set to true.
* popupIconClass: * popupIconClass:
* A string. A class (or space separated list of classes) * A string. A class (or space separated list of classes)
* that will be applied to the icon in the popup so that * that will be applied to the icon in the popup so that
@ -411,10 +407,6 @@ PopupNotifications.prototype = {
throw "PopupNotifications_show: invalid mainAction"; throw "PopupNotifications_show: invalid mainAction";
if (secondaryActions && secondaryActions.some(isInvalidAction)) if (secondaryActions && secondaryActions.some(isInvalidAction))
throw "PopupNotifications_show: invalid secondaryActions"; throw "PopupNotifications_show: invalid secondaryActions";
if (options && options.hideNotNow &&
(!secondaryActions || !secondaryActions.length ||
!secondaryActions.concat(mainAction).some(action => action.dismiss)))
throw "PopupNotifications_show: 'Not Now' item hidden without replacement";
let notification = new Notification(id, message, anchorID, mainAction, let notification = new Notification(id, message, anchorID, mainAction,
secondaryActions, browser, this, options); secondaryActions, browser, this, options);
@ -597,8 +589,7 @@ PopupNotifications.prototype = {
// An explicitly dismissed persistent notification effectively becomes // An explicitly dismissed persistent notification effectively becomes
// non-persistent. // non-persistent.
if (this.panel.firstChild && if (this.panel.firstChild &&
(telemetryReason == TELEMETRY_STAT_DISMISSAL_CLOSE_BUTTON || telemetryReason == TELEMETRY_STAT_DISMISSAL_CLOSE_BUTTON) {
telemetryReason == TELEMETRY_STAT_DISMISSAL_NOT_NOW)) {
this.panel.firstChild.notification.options.persistent = false; this.panel.firstChild.notification.options.persistent = false;
} }
@ -687,18 +678,16 @@ PopupNotifications.prototype = {
popupnotification.setAttribute("buttonlabel", n.mainAction.label); popupnotification.setAttribute("buttonlabel", n.mainAction.label);
popupnotification.setAttribute("buttonaccesskey", n.mainAction.accessKey); popupnotification.setAttribute("buttonaccesskey", n.mainAction.accessKey);
popupnotification.setAttribute("buttoncommand", "PopupNotifications._onButtonEvent(event, 'buttoncommand');"); popupnotification.setAttribute("buttoncommand", "PopupNotifications._onButtonEvent(event, 'buttoncommand');");
popupnotification.setAttribute("buttonpopupshown", "PopupNotifications._onButtonEvent(event, 'buttonpopupshown');"); popupnotification.setAttribute("dropmarkerpopupshown", "PopupNotifications._onButtonEvent(event, 'dropmarkerpopupshown');");
popupnotification.setAttribute("learnmoreclick", "PopupNotifications._onButtonEvent(event, 'learnmoreclick');"); popupnotification.setAttribute("learnmoreclick", "PopupNotifications._onButtonEvent(event, 'learnmoreclick');");
popupnotification.setAttribute("menucommand", "PopupNotifications._onMenuCommand(event);"); popupnotification.setAttribute("menucommand", "PopupNotifications._onMenuCommand(event);");
popupnotification.setAttribute("closeitemcommand", `PopupNotifications._dismiss(${TELEMETRY_STAT_DISMISSAL_NOT_NOW});event.stopPropagation();`);
} else { } else {
popupnotification.removeAttribute("buttonlabel"); popupnotification.removeAttribute("buttonlabel");
popupnotification.removeAttribute("buttonaccesskey"); popupnotification.removeAttribute("buttonaccesskey");
popupnotification.removeAttribute("buttoncommand"); popupnotification.removeAttribute("buttoncommand");
popupnotification.removeAttribute("buttonpopupshown"); popupnotification.removeAttribute("dropmarkerpopupshown");
popupnotification.removeAttribute("learnmoreclick"); popupnotification.removeAttribute("learnmoreclick");
popupnotification.removeAttribute("menucommand"); popupnotification.removeAttribute("menucommand");
popupnotification.removeAttribute("closeitemcommand");
} }
if (n.options.popupIconClass) { if (n.options.popupIconClass) {
@ -729,17 +718,26 @@ PopupNotifications.prototype = {
} else } else
popupnotification.removeAttribute("origin"); popupnotification.removeAttribute("origin");
if (n.options.hideClose)
popupnotification.setAttribute("closebuttonhidden", "true");
popupnotification.notification = n; popupnotification.notification = n;
if (n.secondaryActions) { if (n.secondaryActions && n.secondaryActions.length > 0) {
let telemetryStatId = TELEMETRY_STAT_ACTION_2; let telemetryStatId = TELEMETRY_STAT_ACTION_2;
n.secondaryActions.forEach(function(a) { let secondaryAction = n.secondaryActions[0];
popupnotification.setAttribute("secondarybuttonlabel", secondaryAction.label);
popupnotification.setAttribute("secondarybuttonaccesskey", secondaryAction.accessKey);
popupnotification.setAttribute("secondarybuttoncommand", "PopupNotifications._onButtonEvent(event, 'secondarybuttoncommand');");
for (let i = 1; i < n.secondaryActions.length; i++) {
let action = n.secondaryActions[i];
let item = doc.createElementNS(XUL_NS, "menuitem"); let item = doc.createElementNS(XUL_NS, "menuitem");
item.setAttribute("label", a.label); item.setAttribute("label", action.label);
item.setAttribute("accesskey", a.accessKey); item.setAttribute("accesskey", action.accessKey);
item.notification = n; item.notification = n;
item.action = a; item.action = action;
popupnotification.appendChild(item); popupnotification.appendChild(item);
@ -749,15 +747,14 @@ PopupNotifications.prototype = {
if (telemetryStatId < TELEMETRY_STAT_ACTION_LAST) { if (telemetryStatId < TELEMETRY_STAT_ACTION_LAST) {
telemetryStatId++; telemetryStatId++;
} }
}, this); }
if (n.options.hideNotNow) { if (n.secondaryActions.length < 2) {
popupnotification.setAttribute("hidenotnow", "true"); popupnotification.setAttribute("dropmarkerhidden", "true");
}
else if (n.secondaryActions.length) {
let closeItemSeparator = doc.createElementNS(XUL_NS, "menuseparator");
popupnotification.appendChild(closeItemSeparator);
} }
} else {
popupnotification.setAttribute("secondarybuttonhidden", "true");
popupnotification.setAttribute("dropmarkerhidden", "true");
} }
let checkbox = n.options.checkbox; let checkbox = n.options.checkbox;
@ -788,11 +785,14 @@ PopupNotifications.prototype = {
}, },
_setNotificationUIState(notification, state = {}) { _setNotificationUIState(notification, state = {}) {
notification.setAttribute("mainactiondisabled", state.disableMainAction || "false"); if (state.disableMainAction) {
notification.setAttribute("mainactiondisabled", "true");
} else {
notification.removeAttribute("mainactiondisabled");
}
if (state.warningLabel) { if (state.warningLabel) {
notification.setAttribute("warninglabel", state.warningLabel); notification.setAttribute("warninglabel", state.warningLabel);
notification.setAttribute("warninghidden", "false"); notification.removeAttribute("warninghidden");
} else { } else {
notification.setAttribute("warninghidden", "true"); notification.setAttribute("warninghidden", "true");
} }
@ -1277,7 +1277,7 @@ PopupNotifications.prototype = {
let notification = notificationEl.notification; let notification = notificationEl.notification;
if (type == "buttonpopupshown") { if (type == "dropmarkerpopupshown") {
notification._recordTelemetryStat(TELEMETRY_STAT_OPEN_SUBMENU); notification._recordTelemetryStat(TELEMETRY_STAT_OPEN_SUBMENU);
return; return;
} }
@ -1287,34 +1287,46 @@ PopupNotifications.prototype = {
return; return;
} }
// Record the total timing of the main action since the notification was if (type == "buttoncommand") {
// created, even if the notification was dismissed in the meantime. // Record the total timing of the main action since the notification was
let timeSinceCreated = this.window.performance.now() - notification.timeCreated; // created, even if the notification was dismissed in the meantime.
if (!notification.recordedTelemetryMainAction) { let timeSinceCreated = this.window.performance.now() - notification.timeCreated;
notification.recordedTelemetryMainAction = true; if (!notification.recordedTelemetryMainAction) {
notification._recordTelemetry("POPUP_NOTIFICATION_MAIN_ACTION_MS", notification.recordedTelemetryMainAction = true;
timeSinceCreated); notification._recordTelemetry("POPUP_NOTIFICATION_MAIN_ACTION_MS",
timeSinceCreated);
}
} }
let timeSinceShown = this.window.performance.now() - notification.timeShown; if (type == "buttoncommand" || type == "secondarybuttoncommand") {
if (timeSinceShown < this.buttonDelay) { let timeSinceShown = this.window.performance.now() - notification.timeShown;
Services.console.logStringMessage("PopupNotifications._onButtonEvent: " + if (timeSinceShown < this.buttonDelay) {
"Button click happened before the security delay: " + Services.console.logStringMessage("PopupNotifications._onButtonEvent: " +
timeSinceShown + "ms"); "Button click happened before the security delay: " +
return; timeSinceShown + "ms");
return;
}
} }
notification._recordTelemetryStat(TELEMETRY_STAT_ACTION_1); let action = notification.mainAction;
let telemetryStatId = TELEMETRY_STAT_ACTION_1;
if (type == "secondarybuttoncommand") {
action = notification.secondaryActions[0];
telemetryStatId = TELEMETRY_STAT_ACTION_2;
}
notification._recordTelemetryStat(telemetryStatId);
try { try {
notification.mainAction.callback.call(undefined, { action.callback.call(undefined, {
checkboxChecked: notificationEl.checkbox.checked checkboxChecked: notificationEl.checkbox.checked
}); });
} catch (error) { } catch (error) {
Cu.reportError(error); Cu.reportError(error);
} }
if (notification.mainAction.dismiss) { if (action.dismiss) {
this._dismiss(); this._dismiss();
return; return;
} }

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

@ -110,11 +110,6 @@ notification[type="info"]:not([value="translation"]) .close-icon:not(:hover) {
outline-offset: -2px; outline-offset: -2px;
} }
/* This is changed due to hover transparency looking invisible when buttons are hovered */
.popup-notification-button-wrapper {
--panel-separator-color: white;
}
.popup-notification-warning { .popup-notification-warning {
color: #aa1b08; color: #aa1b08;
} }

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

@ -43,13 +43,11 @@
.popup-notification-button-container { .popup-notification-button-container {
background-color: var(--arrowpanel-dimmed); background-color: var(--arrowpanel-dimmed);
border-top: 1px solid var(--panel-separator-color); border-top: 1px solid var(--panel-separator-color);
display: flex;
justify-content: flex-end;
} }
.popup-notification-button-wrapper { .popup-notification-button-container > toolbarseparator {
background-color: #0996f8;
}
.popup-notification-button-wrapper > toolbarseparator {
-moz-appearance: none; -moz-appearance: none;
border: 0; border: 0;
border-left: 1px solid var(--panel-separator-color); border-left: 1px solid var(--panel-separator-color);
@ -57,41 +55,67 @@
min-width: 0; min-width: 0;
} }
.popup-notification-button-wrapper:hover > toolbarseparator { .popup-notification-button-container:hover > toolbarseparator {
margin: 0; margin: 0;
} }
.popup-notification-button { .popup-notification-button {
flex: 1;
-moz-appearance: none; -moz-appearance: none;
background-color: transparent; background-color: transparent;
color: white; color: inherit;
margin: 0; margin: 0;
padding: 0 18px; padding: 0;
min-height: 40px;
min-width: 0; min-width: 0;
min-height: 40px;
border: none; border: none;
} }
.popup-notification-button:hover { .popup-notification-button:hover:not([disabled]) {
outline: 1px solid var(--arrowpanel-dimmed); outline: 1px solid var(--arrowpanel-dimmed);
background-color: #0675d3; background-color: var(--arrowpanel-dimmed);
} }
.popup-notification-button:hover:active { .popup-notification-button:hover:active:not([disabled]) {
outline: 1px solid var(--arrowpanel-dimmed-further); outline: 1px solid var(--arrowpanel-dimmed-further);
background-color: #0568ba; background-color: var(--arrowpanel-dimmed-further);
box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset; box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
} }
.popup-notification-dropmarker { .popup-notification-button[disabled] {
padding: 0 15px; background-color: var(--arrowpanel-dimmed-further);
color: graytext;
}
.popup-notification-button[default] {
flex: 0 50%;
}
.popup-notification-button[default]:not([disabled]) {
background-color: #0996f8;
color: white;
}
.popup-notification-button[default]:hover:not([disabled]) {
background-color: #0675d3;
}
.popup-notification-button[default]:hover:active:not([disabled]) {
background-color: #0568ba;
} }
/* prevent double border on windows when focused */
.popup-notification-button > .button-box { .popup-notification-button > .button-box {
padding: 0;
margin: 0;
/* prevent double border on windows when focused */
border: none; border: none;
} }
.popup-notification-dropmarker {
flex: none;
padding: 0 15px;
}
.popup-notification-dropmarker > .button-box > hbox { .popup-notification-dropmarker > .button-box > hbox {
display: none; display: none;
} }
@ -100,6 +124,8 @@
/* This is to override the linux !important */ /* This is to override the linux !important */
-moz-appearance: none !important; -moz-appearance: none !important;
display: -moz-box; display: -moz-box;
padding: 0;
margin: 0;
} }
.popup-notification-dropmarker > .button-box > .button-menu-dropmarker > .dropmarker-icon { .popup-notification-dropmarker > .button-box > .button-menu-dropmarker > .dropmarker-icon {
@ -107,9 +133,5 @@
height: 16px; height: 16px;
list-style-image: url(chrome://global/skin/icons/menubutton-dropmarker.svg); list-style-image: url(chrome://global/skin/icons/menubutton-dropmarker.svg);
filter: url(chrome://global/skin/filters.svg#fill); filter: url(chrome://global/skin/filters.svg#fill);
fill: white; fill: currentColor;
}
.popup-notification-button[disabled] {
opacity: 0.5;
} }