Bug 823443 - Provide an easy way to add custom content to popup notifications. r=gavin
authorDão Gottwald <dao@mozilla.com>
Fri, 21 Dec 2012 20:09:07 +0100
changeset 125916 2721852327e867e00ade4386d9839ab4b21b8331
parent 125915 a678066eaadd120fe95cc0cbb5ee8f6d91d56a75
child 125917 4b9f2a81751e752b9c2ecc9a4631d467684332c9
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin
bugs823443
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 823443 - Provide an easy way to add custom content to popup notifications. r=gavin
toolkit/content/PopupNotifications.jsm
toolkit/content/widgets/notification.xml
--- a/toolkit/content/PopupNotifications.jsm
+++ b/toolkit/content/PopupNotifications.jsm
@@ -11,16 +11,17 @@ Components.utils.import("resource://gre/
 const NOTIFICATION_EVENT_DISMISSED = "dismissed";
 const NOTIFICATION_EVENT_REMOVED = "removed";
 const NOTIFICATION_EVENT_SHOWN = "shown";
 
 const ICON_SELECTOR = ".notification-anchor-icon";
 const ICON_ATTRIBUTE_SHOWING = "showing";
 
 let popupNotificationsMap = new WeakMap();
+let gNotificationParents = new WeakMap;
 
 /**
  * Notification object describes a single popup notification.
  *
  * @see PopupNotifications.show()
  */
 function Notification(id, message, anchorID, mainAction, secondaryActions,
                       browser, owner, options) {
@@ -390,31 +391,71 @@ PopupNotifications.prototype = {
    */
   _hidePanel: function PopupNotifications_hide() {
     this._ignoreDismissal = true;
     this.panel.hidePopup();
     this._ignoreDismissal = false;
   },
 
   /**
-   *
+   * Removes all notifications from the notification popup.
    */
+  _clearPanel: function () {
+    let popupnotification;
+    while ((popupnotification = this.panel.lastChild)) {
+      this.panel.removeChild(popupnotification);
+
+      // If this notification was provided by the chrome document rather than
+      // created ad hoc, move it back to where we got it from.
+      let originalParent = gNotificationParents.get(popupnotification);
+      if (originalParent) {
+        popupnotification.notification = null;
+
+        // Remove nodes dynamically added to the notification's menu button
+        // in _refreshPanel. Keep popupnotificationcontent nodes; they are
+        // provided by the chrome document.
+        let contentNode = popupnotification.lastChild;
+        while (contentNode) {
+          let previousSibling = contentNode.previousSibling;
+          if (contentNode.nodeName != "popupnotificationcontent")
+            popupnotification.removeChild(contentNode);
+          contentNode = previousSibling;
+        }
+
+        // Re-hide the notification such that it isn't rendered in the chrome
+        // document. _refreshPanel will unhide it again when needed.
+        popupnotification.hidden = true;
+
+        originalParent.appendChild(popupnotification);
+      }
+    }
+  },
+
   _refreshPanel: function PopupNotifications_refreshPanel(notificationsToShow) {
-    while (this.panel.lastChild)
-      this.panel.removeChild(this.panel.lastChild);
+    this._clearPanel();
 
     const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
     notificationsToShow.forEach(function (n) {
       let doc = this.window.document;
-      let popupnotification = doc.createElementNS(XUL_NS, "popupnotification");
-      popupnotification.setAttribute("label", n.message);
+
       // Append "-notification" to the ID to try to avoid ID conflicts with other stuff
       // in the document.
-      popupnotification.setAttribute("id", n.id + "-notification");
+      let popupnotificationID = n.id + "-notification";
+
+      // If the chrome document provides a popupnotification with this id, use
+      // that. Otherwise create it ad-hoc.
+      let popupnotification = doc.getElementById(popupnotificationID);
+      if (popupnotification)
+        gNotificationParents.set(popupnotification, popupnotification.parentNode);
+      else
+        popupnotification = doc.createElementNS(XUL_NS, "popupnotification");
+
+      popupnotification.setAttribute("label", n.message);
+      popupnotification.setAttribute("id", popupnotificationID);
       popupnotification.setAttribute("popupid", n.id);
       popupnotification.setAttribute("closebuttoncommand", "PopupNotifications._dismiss();");
       if (n.mainAction) {
         popupnotification.setAttribute("buttonlabel", n.mainAction.label);
         popupnotification.setAttribute("buttonaccesskey", n.mainAction.accessKey);
         popupnotification.setAttribute("buttoncommand", "PopupNotifications._onButtonCommand(event);");
         popupnotification.setAttribute("menucommand", "PopupNotifications._onMenuCommand(event);");
         popupnotification.setAttribute("closeitemcommand", "PopupNotifications._dismiss();event.stopPropagation();");
@@ -436,16 +477,20 @@ PopupNotifications.prototype = {
 
         if (n.secondaryActions.length) {
           let closeItemSeparator = doc.createElementNS(XUL_NS, "menuseparator");
           popupnotification.appendChild(closeItemSeparator);
         }
       }
 
       this.panel.appendChild(popupnotification);
+
+      // The popupnotification may be hidden if we got it from the chrome
+      // document rather than creating it ad hoc.
+      popupnotification.hidden = false;
     }, this);
   },
 
   _showPanel: function PopupNotifications_showPanel(notificationsToShow, anchorElement) {
     this.panel.hidden = false;
 
     this._refreshPanel(notificationsToShow);
 
@@ -617,18 +662,17 @@ PopupNotifications.prototype = {
       if (notificationObj.options.removeOnDismissal)
         this._remove(notificationObj);
       else {
         notificationObj.dismissed = true;
         this._fireCallback(notificationObj, NOTIFICATION_EVENT_DISMISSED);
       }
     }, this);
 
-    while (this.panel.lastChild)
-      this.panel.removeChild(this.panel.lastChild);
+    this._clearPanel();
 
     this._update();
   },
 
   _onButtonCommand: function PopupNotifications_onButtonCommand(event) {
     // Need to find the associated notification object, which is a bit tricky
     // since it isn't associated with the button directly - this is kind of
     // gross and very dependent on the structure of the popupnotification
--- a/toolkit/content/widgets/notification.xml
+++ b/toolkit/content/widgets/notification.xml
@@ -444,16 +444,17 @@
 
   <binding id="popup-notification">
     <content align="start">
       <xul:image class="popup-notification-icon"
                  xbl:inherits="popupid,src=icon"/>
       <xul:vbox flex="1">
         <xul:description class="popup-notification-description"
                          xbl:inherits="xbl:text=label"/>
+        <children includes="popupnotificationcontent"/>
         <xul:spacer flex="1"/>
         <xul:hbox class="popup-notification-button-container"
                   pack="end" align="center">
           <xul:button anonid="button"
                       class="popup-notification-menubutton"
                       type="menu-button"
                       xbl:inherits="oncommand=buttoncommand,label=buttonlabel,accesskey=buttonaccesskey">
             <xul:menupopup anonid="menupopup"