Bug 1388183 - Queue and play the last download notification instead of truncating/aborting it. r=paolo
* Maintain a _nextNotificationType LIFO "queue" of one, to play overlapping events in series
* _showNotification assumes everything could have changed since it was last called
* Signature of showEventNotification is unchanged, but most of its work is now done in _showNotification
MozReview-Commit-ID: 8sv4DZoZU65
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -273,48 +273,72 @@ const DownloadsIndicatorView = {
aCallback();
}
});
},
// Direct control functions
/**
- * Set while we are waiting for a notification to fade out.
+ * Set to the type ("start" or "finish") when display of a notification is in-progress
*/
- _notificationTimeout: null,
+ _currentNotificationType: null,
+
+ /**
+ * Set to the type ("start" or "finish") when a notification arrives while we
+ * are waiting for the timeout of the previous notification
+ */
+ _nextNotificationType: null,
/**
* Check if the panel containing aNode is open.
* @param aNode
* the node whose panel we're interested in.
*/
_isAncestorPanelOpen(aNode) {
while (aNode && aNode.localName != "panel") {
aNode = aNode.parentNode;
}
return aNode && aNode.state == "open";
},
/**
- * If the status indicator is visible in its assigned position, shows for a
- * brief time a visual notification of a relevant event, like a new download.
+ * Display or enqueue a visual notification of a relevant event, like a new download.
*
* @param aType
* Set to "start" for new downloads, "finish" for completed downloads.
*/
showEventNotification(aType) {
if (!this._initialized) {
return;
}
if (!DownloadsCommon.animateNotifications) {
return;
}
+ // enqueue this notification while the current one is being displayed
+ if (this._currentNotificationType) {
+ // only queue up the notification if it is different to the current one
+ if (this._currentNotificationType != aType) {
+ this._nextNotificationType = aType;
+ }
+ } else {
+ this._showNotification(aType);
+ }
+ },
+
+ /**
+ * If the status indicator is visible in its assigned position, shows for a
+ * brief time a visual notification of a relevant event, like a new download.
+ *
+ * @param aType
+ * Set to "start" for new downloads, "finish" for completed downloads.
+ */
+ _showNotification(aType) {
// No need to show visual notification if the panel is visible.
if (DownloadsPanel.isPanelShowing) {
return;
}
let anchor = DownloadsButton._placeholder;
let widgetGroup = CustomizableUI.getWidget("downloads-button");
let widget = widgetGroup.forWindow(window);
@@ -329,20 +353,16 @@ const DownloadsIndicatorView = {
// Otherwise, try to use the anchor of the panel:
anchor = widget.anchor;
}
if (!anchor || !isElementVisible(anchor.parentNode)) {
// Our container isn't visible, so can't show the animation:
return;
}
- if (this._notificationTimeout) {
- clearTimeout(this._notificationTimeout);
- }
-
// The notification element is positioned to show in the same location as
// the downloads button. It's not in the downloads button itself in order to
// be able to anchor the notification elsewhere if required, and to ensure
// the notification isn't clipped by overflow properties of the anchor's
// container.
// Note: no notifier animation for download finished in Photon
let notifier = this.notifier;
@@ -366,21 +386,34 @@ const DownloadsIndicatorView = {
notifier.setAttribute("notification", aType);
}
anchor.setAttribute("notification", aType);
let animationDuration;
// This value is determined by the overall duration of animation in CSS.
animationDuration = aType == "start" ? 760 : 850;
- this._notificationTimeout = setTimeout(() => {
- notifier.setAttribute("hidden", "true");
- notifier.removeAttribute("notification");
- notifier.style.transform = "";
- anchor.removeAttribute("notification");
+ this._currentNotificationType = aType;
+
+ setTimeout(() => {
+ requestAnimationFrame(() => {
+ notifier.setAttribute("hidden", "true");
+ notifier.removeAttribute("notification");
+ notifier.style.transform = "";
+ anchor.removeAttribute("notification");
+
+ requestAnimationFrame(() => {
+ let nextType = this._nextNotificationType;
+ this._currentNotificationType = null;
+ this._nextNotificationType = null;
+ if (nextType) {
+ this._showNotification(nextType);
+ }
+ });
+ });
}, animationDuration);
},
// Callback functions from DownloadsIndicatorData
/**
* Indicates whether the indicator should be shown because there are some
* downloads to be displayed.