Bug 1004061 - Make most doorhanger notifications persistent. r=florian

MozReview-Commit-ID: IEPkDRnYpiM

--HG--
extra : rebase_source : 59ba5ffcd41d80ad5e4a0e3b9cd05f5b3a4308a3
This commit is contained in:
Panos Astithas 2016-11-20 18:40:04 +01:00
Родитель 660f174d3a
Коммит 55ed3317db
14 изменённых файлов: 265 добавлений и 144 удалений

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

@ -88,10 +88,10 @@ const gXPInstallObserver = {
const anchorID = "addons-notification-icon";
// Make notifications persist a minimum of 30 seconds
// Make notifications persistent
var options = {
displayURI: installInfo.originatingURI,
timeout: Date.now() + 30000,
persistent: true,
};
let cancelInstallation = () => {
@ -230,9 +230,10 @@ const gXPInstallObserver = {
var brandShortName = brandBundle.getString("brandShortName");
var notificationID = aTopic;
// Make notifications persist a minimum of 30 seconds
// Make notifications persistent
var options = {
displayURI: installInfo.originatingURI,
persistent: true,
timeout: Date.now() + 30000,
};
@ -567,7 +568,7 @@ var LightWeightThemeWebInstaller = {
};
let options = {
timeout: Date.now() + 30000
persistent: true
};
PopupNotifications.show(gBrowser.selectedBrowser, "addon-theme-change",

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

@ -278,6 +278,7 @@ var gPluginHandler = {
let options = {
dismissed: !showNow,
persistent: showNow,
eventCallback: this._clickToPlayNotificationEventCallback,
primaryPlugin: primaryPluginPermission,
pluginData: pluginData,

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

@ -5971,7 +5971,7 @@ var OfflineApps = {
let anchorID = "indexedDB-notification-icon";
PopupNotifications.show(browser, "offline-app-usage", message,
anchorID, mainAction);
anchorID, mainAction, null, { persistent: true });
// Now that we've warned once, prevent the warning from showing up
// again.
@ -6051,6 +6051,7 @@ var OfflineApps = {
[host]);
let anchorID = "indexedDB-notification-icon";
let options = {
persistent: true,
controlledItems : [[Cu.getWeakReference(browser), docId, uri]]
};
notification = PopupNotifications.show(browser, notificationID, message,
@ -6142,18 +6143,12 @@ var IndexedDBPromptHelper = {
responseTopic = this._permissionsResponse;
}
const hiddenTimeoutDuration = 30000; // 30 seconds
const firstTimeoutDuration = 300000; // 5 minutes
var timeoutId;
var observer = requestor.getInterface(Ci.nsIObserver);
var mainAction = {
label: gNavigatorBundle.getString("offlineApps.allow"),
accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
callback: function() {
clearTimeout(timeoutId);
observer.observe(null, responseTopic,
Ci.nsIPermissionManager.ALLOW_ACTION);
}
@ -6164,61 +6159,15 @@ var IndexedDBPromptHelper = {
label: gNavigatorBundle.getString("offlineApps.never"),
accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
callback: function() {
clearTimeout(timeoutId);
observer.observe(null, responseTopic,
Ci.nsIPermissionManager.DENY_ACTION);
}
}
];
// This will be set to the result of PopupNotifications.show().
var notification;
function timeoutNotification() {
// Remove the notification.
if (notification) {
notification.remove();
}
// Clear all of our timeout stuff. We may be called directly, not just
// when the timeout actually elapses.
clearTimeout(timeoutId);
// And tell the page that the popup timed out.
observer.observe(null, responseTopic,
Ci.nsIPermissionManager.UNKNOWN_ACTION);
}
var options = {
eventCallback: function(state) {
// Don't do anything if the timeout has not been set yet.
if (!timeoutId) {
return;
}
// If the popup is being dismissed start the short timeout.
if (state == "dismissed") {
clearTimeout(timeoutId);
timeoutId = setTimeout(timeoutNotification, hiddenTimeoutDuration);
return;
}
// If the popup is being re-shown then clear the timeout allowing
// unlimited waiting.
if (state == "shown") {
clearTimeout(timeoutId);
}
}
};
notification = PopupNotifications.show(browser, topic, message,
PopupNotifications.show(browser, topic, message,
this._notificationIcon, mainAction,
secondaryActions, options);
// Set the timeoutId after the popup has been created, and use the long
// timeout value. If the user doesn't notice the popup after this amount of
// time then it is most likely not visible and we want to alert the page.
timeoutId = setTimeout(timeoutNotification, firstTimeoutDuration);
secondaryActions, { persistent: true });
}
};

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

@ -116,13 +116,13 @@ function waitForNotification(aId, aExpectedCount = 1) {
yield observerPromise;
yield panelEventPromise;
info("Saw a notification");
info("Saw a " + aId + " notification");
ok(PopupNotifications.isPanelOpen, "Panel should be open");
is(PopupNotifications.panel.childNodes.length, aExpectedCount, "Should be the right number of notifications");
if (PopupNotifications.panel.childNodes.length) {
let nodes = Array.from(PopupNotifications.panel.childNodes);
let notification = nodes.find(n => n.id == aId + "-notification");
ok(notification, `Should have seen the right notification`);
ok(notification, "Should have seen the " + aId + " notification");
}
return PopupNotifications.panel;
@ -512,7 +512,9 @@ function test_multiple() {
function test_sequential() {
return Task.spawn(function* () {
// This test is only relevant if using the new doorhanger UI
if (!Preferences.get("xpinstall.customConfirmationUI", false)) {
// TODO: this subtest is disabled until multiple notification prompts are
// reworked in bug 1188152
if (true || !Preferences.get("xpinstall.customConfirmationUI", false)) {
return;
}
let pm = Services.perms;

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

@ -12,6 +12,8 @@ skip-if = (os == "linux" && (debug || asan))
skip-if = (os == "linux" && (debug || asan))
[browser_popupNotification_4.js]
skip-if = (os == "linux" && (debug || asan))
[browser_popupNotification_5.js]
skip-if = (os == "linux" && (debug || asan))
[browser_popupNotification_checkbox.js]
skip-if = (os == "linux" && (debug || asan))
[browser_reshow_in_background.js]

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

@ -227,68 +227,5 @@ var tests = [
notification.remove();
goNext();
}
},
// panel updates should fire the showing and shown callbacks again.
{ id: "Test#11",
run: function() {
this.notifyObj = new BasicNotification(this.id);
this.notification = showNotification(this.notifyObj);
},
onShown: function(popup) {
checkPopup(popup, this.notifyObj);
this.notifyObj.showingCallbackTriggered = false;
this.notifyObj.shownCallbackTriggered = false;
// Force an update of the panel. This is typically called
// automatically when receiving 'activate' or 'TabSelect' events,
// but from a setTimeout, which is inconvenient for the test.
PopupNotifications._update();
checkPopup(popup, this.notifyObj);
this.notification.remove();
},
onHidden: function() { }
},
// A first dismissed notification shouldn't stop _update from showing a second notification
{ id: "Test#12",
run: function() {
this.notifyObj1 = new BasicNotification(this.id);
this.notifyObj1.id += "_1";
this.notifyObj1.anchorID = "default-notification-icon";
this.notifyObj1.options.dismissed = true;
this.notification1 = showNotification(this.notifyObj1);
this.notifyObj2 = new BasicNotification(this.id);
this.notifyObj2.id += "_2";
this.notifyObj2.anchorID = "geo-notification-icon";
this.notifyObj2.options.dismissed = true;
this.notification2 = showNotification(this.notifyObj2);
this.notification2.dismissed = false;
PopupNotifications._update();
},
onShown: function(popup) {
checkPopup(popup, this.notifyObj2);
this.notification1.remove();
this.notification2.remove();
},
onHidden: function(popup) { }
},
// The anchor icon should be shown for notifications in background windows.
{ id: "Test#13",
run: function() {
let notifyObj = new BasicNotification(this.id);
notifyObj.options.dismissed = true;
let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
whenDelayedStartupFinished(win, function() {
showNotification(notifyObj);
let anchor = document.getElementById("default-notification-icon");
is(anchor.getAttribute("showing"), "true", "the anchor is shown");
win.close();
goNext();
});
}
}
];

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

@ -0,0 +1,205 @@
/* 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/. */
function test() {
waitForExplicitFinish();
ok(PopupNotifications, "PopupNotifications object exists");
ok(PopupNotifications.panel, "PopupNotifications panel exists");
setup();
goNext();
}
var gNotification;
var tests = [
// panel updates should fire the showing and shown callbacks again.
{ id: "Test#1",
run: function() {
this.notifyObj = new BasicNotification(this.id);
this.notification = showNotification(this.notifyObj);
},
onShown: function(popup) {
checkPopup(popup, this.notifyObj);
this.notifyObj.showingCallbackTriggered = false;
this.notifyObj.shownCallbackTriggered = false;
// Force an update of the panel. This is typically called
// automatically when receiving 'activate' or 'TabSelect' events,
// but from a setTimeout, which is inconvenient for the test.
PopupNotifications._update();
checkPopup(popup, this.notifyObj);
this.notification.remove();
},
onHidden: function() { }
},
// A first dismissed notification shouldn't stop _update from showing a second notification
{ id: "Test#2",
run: function() {
this.notifyObj1 = new BasicNotification(this.id);
this.notifyObj1.id += "_1";
this.notifyObj1.anchorID = "default-notification-icon";
this.notifyObj1.options.dismissed = true;
this.notification1 = showNotification(this.notifyObj1);
this.notifyObj2 = new BasicNotification(this.id);
this.notifyObj2.id += "_2";
this.notifyObj2.anchorID = "geo-notification-icon";
this.notifyObj2.options.dismissed = true;
this.notification2 = showNotification(this.notifyObj2);
this.notification2.dismissed = false;
PopupNotifications._update();
},
onShown: function(popup) {
checkPopup(popup, this.notifyObj2);
this.notification1.remove();
this.notification2.remove();
},
onHidden: function(popup) { }
},
// The anchor icon should be shown for notifications in background windows.
{ id: "Test#3",
run: function() {
let notifyObj = new BasicNotification(this.id);
notifyObj.options.dismissed = true;
let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
whenDelayedStartupFinished(win, function() {
showNotification(notifyObj);
let anchor = document.getElementById("default-notification-icon");
is(anchor.getAttribute("showing"), "true", "the anchor is shown");
win.close();
goNext();
});
}
},
// Test that persistent doesn't allow the notification to persist after
// navigation.
{ id: "Test#4",
run: function* () {
this.oldSelectedTab = gBrowser.selectedTab;
gBrowser.selectedTab = gBrowser.addTab("about:blank");
yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.addOptions({
persistent: true
});
this.notification = showNotification(this.notifyObj);
},
onShown: function* (popup) {
this.complete = false;
yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.org/");
yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
// This code should not be executed.
ok(false, "Should have removed the notification after navigation");
// Properly dismiss and cleanup in case the unthinkable happens.
this.complete = true;
triggerSecondaryCommand(popup, 1);
},
onHidden: function(popup) {
ok(!this.complete, "Should have hidden the notification after navigation");
this.notification.remove();
gBrowser.removeTab(gBrowser.selectedTab);
gBrowser.selectedTab = this.oldSelectedTab;
}
},
// Test that persistent allows the notification to persist until explicitly
// dismissed.
{ id: "Test#5",
run: function* () {
this.oldSelectedTab = gBrowser.selectedTab;
gBrowser.selectedTab = gBrowser.addTab("about:blank");
yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.addOptions({
persistent: true
});
this.notification = showNotification(this.notifyObj);
},
onShown: function* (popup) {
this.complete = false;
// Notification should persist after attempt to dismiss by clicking on the
// content area.
let browser = gBrowser.selectedBrowser;
yield BrowserTestUtils.synthesizeMouseAtCenter("body", {}, browser)
// Notification should be hidden after dismissal via Not Now.
this.complete = true;
triggerSecondaryCommand(popup, 1);
},
onHidden: function(popup) {
ok(this.complete, "Should have hidden the notification after clicking Not Now");
this.notification.remove();
gBrowser.removeTab(gBrowser.selectedTab);
gBrowser.selectedTab = this.oldSelectedTab;
}
},
// Test that persistent panels are still open after switching to another tab
// and back.
{ id: "Test#6a",
run: function* () {
this.notifyObj = new BasicNotification(this.id);
this.notifyObj.options.persistent = true;
gNotification = showNotification(this.notifyObj);
},
onShown: function* (popup) {
this.oldSelectedTab = gBrowser.selectedTab;
gBrowser.selectedTab = gBrowser.addTab("about:blank");
info("Waiting for the new tab to load.");
yield promiseTabLoadEvent(gBrowser.selectedTab, "http://example.com/");
},
onHidden: function(popup) {
ok(true, "Should have hidden the notification after tab switch");
gBrowser.removeTab(gBrowser.selectedTab);
gBrowser.selectedTab = this.oldSelectedTab;
}
},
// Second part of the previous test that compensates for the limitation in
// runNextTest that expects a single onShown/onHidden invocation per test.
{ id: "Test#6b",
run: function* () {
let id = PopupNotifications.panel.firstChild.getAttribute("popupid");
ok(id.endsWith("Test#6a"), "Should have found the notification from Test6a");
ok(PopupNotifications.isPanelOpen, "Should have shown the popup again after getting back to the tab");
gNotification.remove();
gNotification = null;
goNext();
}
},
// Test that persistent panels are still open after switching to another
// window and back.
{ id: "Test#7",
run: function* () {
this.oldSelectedTab = gBrowser.selectedTab;
gBrowser.selectedTab = gBrowser.addTab("about:blank");
let notifyObj = new BasicNotification(this.id);
notifyObj.options.persistent = true;
this.notification = showNotification(notifyObj);
let win = gBrowser.replaceTabWithWindow(gBrowser.addTab("about:blank"));
whenDelayedStartupFinished(win, () => {
ok(notifyObj.shownCallbackTriggered, "Should have triggered the shown callback");
let anchor = win.document.getElementById("default-notification-icon");
win.PopupNotifications._reshowNotifications(anchor);
ok(win.PopupNotifications.panel.childNodes.length == 0,
"no notification displayed in new window");
ok(PopupNotifications.isPanelOpen, "Should be still showing the popup in the first window");
win.close();
let id = PopupNotifications.panel.firstChild.getAttribute("popupid");
ok(id.endsWith("Test#7"), "Should have found the notification from Test7");
ok(PopupNotifications.isPanelOpen, "Should have shown the popup again after getting back to the window");
this.notification.remove();
gBrowser.removeTab(gBrowser.selectedTab);
gBrowser.selectedTab = this.oldSelectedTab;
goNext();
});
}
}
];

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

@ -115,11 +115,6 @@ function activateOneProvider(manifest, finishActivation, aCallback) {
let panel = document.getElementById("servicesInstall-notification");
BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown").then(() => {
ok(!panel.hidden, "servicesInstall-notification panel opened");
if (finishActivation)
panel.button.click();
else
panel.closebutton.click();
});
BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden").then(() => {
ok(panel.hidden, "servicesInstall-notification panel hidden");
if (!finishActivation) {
@ -132,6 +127,11 @@ function activateOneProvider(manifest, finishActivation, aCallback) {
});
}
});
if (finishActivation)
panel.button.click();
else
panel.closebutton.click();
});
// the test will continue as the popup events fire...
activateProvider(manifest.origin, function() {

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

@ -2854,6 +2854,7 @@ var E10SAccessibilityCheck = {
let options = {
popupIconURL: "chrome://browser/skin/e10s-64@2x.png",
learnMoreURL: Services.urlFormatter.formatURLPref("app.support.e10sAccessibilityUrl"),
persistent: true,
persistWhileVisible: true,
hideNotNow: true,
};

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

@ -337,6 +337,7 @@ this.PermissionPromptPrototype = {
if (!options.hasOwnProperty('displayURI') || options.displayURI) {
options.displayURI = this.principal.URI;
}
options.persistent = true;
this.onBeforeShow();
chromeWin.PopupNotifications.show(this.browser,

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

@ -567,6 +567,7 @@ this.SocialService = {
let options = {
learnMoreURL: Services.urlFormatter.formatURLPref("app.support.baseURL") + "social-api",
persistent: true,
};
let anchor = "servicesInstall-notification-icon";
let notificationid = "servicesInstall";

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

@ -356,6 +356,7 @@ function prompt(aBrowser, aRequest) {
}
let options = {
persistent: true,
eventCallback: function(aTopic, aNewBrowser) {
if (aTopic == "swapping")
return true;

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

@ -957,9 +957,9 @@ LoginManagerPrompter.prototype = {
mainAction,
secondaryActions,
{
timeout: Date.now() + 10000,
displayURI: Services.io.newURI(login.hostname, null, null),
persistWhileVisible: true,
persistent: true,
passwordNotificationType: type,
eventCallback: function(topic) {
switch (topic) {

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

@ -309,6 +309,11 @@ PopupNotifications.prototype = {
* persistWhileVisible:
* A boolean. If true, a visible notification will always
* persist across location changes.
* persistent: A boolean. If true, the notification will always
* persist even across tab and app changes (but not across
* location changes), until the user accepts or rejects
* the request. The notification will never be implicitly
* dismissed.
* dismissed: Whether the notification should be added as a dismissed
* notification. Dismissed notifications can be activated
* by clicking on their anchorElement.
@ -589,6 +594,14 @@ PopupNotifications.prototype = {
this.nextDismissReason = telemetryReason;
}
// An explicitly dismissed persistent notification effectively becomes
// non-persistent.
if (this.panel.firstChild &&
(telemetryReason == TELEMETRY_STAT_DISMISSAL_CLOSE_BUTTON ||
telemetryReason == TELEMETRY_STAT_DISMISSAL_NOT_NOW)) {
this.panel.firstChild.notification.options.persistent = false;
}
let browser = this.panel.firstChild &&
this.panel.firstChild.notification.browser;
this.panel.hidePopup();
@ -831,7 +844,7 @@ PopupNotifications.prototype = {
// If the panel is already open but we're changing anchors, we need to hide
// it first. Otherwise it can appear in the wrong spot. (_hidePanel is
// safe to call even if the panel is already hidden.)
let promise = this._hidePanel().then(() => {
this._hidePanel().then(() => {
// If the anchor element is hidden or null, use the tab as the anchor. We
// only ever show notifications for the current browser, so we can just use
// the current tab.
@ -846,6 +859,12 @@ PopupNotifications.prototype = {
this._currentAnchorElement = anchorElement;
if (notificationsToShow.some(n => n.options.persistent)) {
this.panel.setAttribute("noautohide", "true");
} else {
this.panel.removeAttribute("noautohide");
}
// On OS X and Linux we need a different panel arrow color for
// click-to-play plugins, so copy the popupid and use css.
this.panel.setAttribute("popupid", this.panel.firstChild.getAttribute("popupid"));
@ -931,9 +950,10 @@ PopupNotifications.prototype = {
}
}
// Filter out notifications that have been dismissed.
// Filter out notifications that have been dismissed, unless they are
// persistent.
let notificationsToShow = notifications.filter(function(n) {
return !n.dismissed && !n.options.neverShow;
return (!n.dismissed || n.options.persistent) && !n.options.neverShow;
});
if (useIconBox) {
@ -1111,7 +1131,7 @@ PopupNotifications.prototype = {
}
// Ensure we move focus into the panel because it's opened through user interaction:
this.panel.removeAttribute("noautofocus", "true");
this.panel.removeAttribute("noautofocus");
this._reshowNotifications(anchor);
},