Bug 1497544 - de-XBL folderSummary-popup, folderSummary, folderSummary-message. r=Paenglab
authorMagnus Melin <mkmelin+mozilla@iki.fi>
Sun, 04 Nov 2018 22:00:45 +0200
changeset 32802 bd1dfa52d616bce5e6024b8a6c2524f0658e6205
parent 32801 d4e98be4fc0b4bbc5f65149e874048b600daf21e
child 32803 0da9137bc4307720dc76acf33817468dc285fb8c
push id2343
push userclokep@gmail.com
push dateMon, 10 Dec 2018 21:37:21 +0000
treeherdercomm-beta@a0750c375f71 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersPaenglab
bugs1497544
Bug 1497544 - de-XBL folderSummary-popup, folderSummary, folderSummary-message. r=Paenglab
chat/content/imtooltip.xml
common/content/customElements.js
mail/base/content/foldersummary.js
mail/base/content/mailWidgets.js
mail/base/content/mailWidgets.xml
mail/base/content/messenger.css
mail/base/content/messenger.xul
mail/base/jar.mn
mail/themes/linux/mail/newmailalert.css
mail/themes/windows/mail/newmailalert.css
mailnews/base/content/newmailalert.js
mailnews/base/content/newmailalert.xul
--- a/chat/content/imtooltip.xml
+++ b/chat/content/imtooltip.xml
@@ -499,17 +499,17 @@
            // we are pointing at carries a title set by the prpl,
            // that title won't be overridden.
            node.setAttribute("title", text);
            break;
          }
 
          // Use the default content tooltip.
          largeTooltip.hidden = true;
-         return fillInPageTooltip(elt);
+         return false;
        ]]>
      </handler>
      <handler event="popuphiding">
        <![CDATA[
        this.buddy = null;
        this.contact = null;
        this.removeAttribute("noTopic");
        this.removeAttribute("left");
--- a/common/content/customElements.js
+++ b/common/content/customElements.js
@@ -5,11 +5,12 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 for (let script of [
   "chrome://messenger/content/mailWidgets.js",
   "chrome://messenger/content/generalBindings.js",
   "chrome://messenger/content/statuspanel.js",
+  "chrome://messenger/content/foldersummary.js",
 ]) {
   Services.scriptloader.loadSubScript(script, window);
 }
new file mode 100644
--- /dev/null
+++ b/mail/base/content/foldersummary.js
@@ -0,0 +1,280 @@
+/**
+ * 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/. */
+
+/* globals MozXULElement */
+/* global gFolderTreeView */
+
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource:///modules/MailServices.jsm");
+ChromeUtils.import("resource:///modules/MailUtils.jsm");
+
+/**
+ * MozFolderSummary displays a listing of NEW mails for the folder in question.
+ * For each mail the subject, sender and a message preview can be included.
+ */
+class MozFolderSummary extends MozXULElement {
+  constructor() {
+    super();
+    this.maxMsgHdrsInPopup = 8;
+
+    this.showSubject = Services.prefs.getBoolPref("mail.biff.alert.show_subject");
+    this.showSender = Services.prefs.getBoolPref("mail.biff.alert.show_sender");
+    this.showPreview = Services.prefs.getBoolPref("mail.biff.alert.show_preview");
+  }
+
+  hasMessages() {
+    return this.hasChildNodes();
+  }
+
+  parseFolder(folder, urlListener, outAsync) {
+    // skip servers, Trash, Junk folders and newsgroups
+    if (!folder || folder.isServer || !folder.hasNewMessages ||
+        folder.getFlag(Ci.nsMsgFolderFlags.Junk) ||
+        folder.getFlag(Ci.nsMsgFolderFlags.Trash) ||
+        (folder.server instanceof Ci.nsINntpIncomingServer)) {
+      return false;
+    }
+
+    let folderArray = [];
+    let msgDatabase;
+    try {
+      msgDatabase = folder.msgDatabase;
+    } catch(e) {
+      // The database for this folder may be missing (e.g. outdated/missing .msf),
+      // so just skip this folder.
+      return false;
+    }
+
+    if (folder.flags & Ci.nsMsgFolderFlags.Virtual) {
+      let srchFolderUri = msgDatabase.dBFolderInfo.getCharProperty("searchFolderUri");
+      let folderUris = srchFolderUri.split("|");
+      let RDF = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
+      for (let uri of folderUris) {
+        let realFolder = RDF.GetResource(uri).QueryInterface(Ci.nsIMsgFolder);
+        if (!realFolder.isServer) {
+          folderArray.push(realFolder);
+        }
+      }
+    } else {
+      folderArray.push(folder);
+    }
+
+    let foundNewMsg = false;
+    for (let folder of folderArray) {
+      // now get the database
+      try {
+        msgDatabase = folder.msgDatabase;
+      } catch(e) {
+        // The database for this folder may be missing (e.g. outdated/missing .msf),
+        // then just skip this folder.
+        continue;
+      }
+
+      folder.msgDatabase = null;
+      let msgKeys = {};
+      let numMsgKeys = {};
+      msgDatabase.getNewList(numMsgKeys, msgKeys);
+
+      if (!numMsgKeys.value) {
+        continue;
+      }
+
+      if (this.showPreview) {
+        // fetchMsgPreviewText forces the previewText property to get generated
+        // for each of the message keys.
+        try {
+          outAsync.value = folder.fetchMsgPreviewText(msgKeys.value,
+            numMsgKeys.value, false, urlListener);
+          folder.msgDatabase = null;
+        } catch (ex) {
+          // fetchMsgPreviewText throws an error when we call it on a news
+          // folder
+          folder.msgDatabase = null;
+          continue;
+        }
+      }
+      // If fetching the preview text is going to be an asynch operation and the
+      // caller is set up to handle that fact, then don't bother filling in any
+      // of the fields since we'll have to do this all over again when the fetch
+      // for the preview text completes.
+      // We don't expect to get called with a urlListener if we're doing a
+      // virtual folder.
+      if (outAsync.value && urlListener) {
+        return false;
+      }
+
+      foundNewMsg = true;
+
+      for (let i = 0; i < this.maxMsgHdrsInPopup && i < numMsgKeys.value; i++) {
+        let msgPopup = this._folderSummaryMessagePopup();
+        let msgHdr = msgDatabase.GetMsgHdrForKey(msgKeys.value[i]);
+        msgPopup.addEventListener("click", (event) => {
+          if (event.button !== 0) {
+            return;
+          }
+          MailUtils.displayMessageInFolderTab(msgHdr);
+          if (gAlertListener) {
+            gAlertListener.observe(null, "alertclickcallback", "");
+          }
+        });
+
+        if (this.showSubject) {
+          let msgSubject = msgHdr.mime2DecodedSubject;
+          const kMsgFlagHasRe = 0x0010; // MSG_FLAG_HAS_RE
+          if (msgHdr.flags & kMsgFlagHasRe) {
+            msgSubject = (msgSubject) ? "Re: " + msgSubject : "Re: ";
+          }
+          msgPopup.querySelector(".folderSummary-subject").textContent = msgSubject;
+        }
+
+        if (this.showSender) {
+          let addrs = MailServices.headerParser.parseEncodedHeader(
+            msgHdr.author, msgHdr.effectiveCharset, false);
+          msgPopup.querySelector(".folderSummary-sender").textContent =
+            (addrs.length > 0) ? (addrs[0].name || addrs[0].email) : "";
+        }
+
+        if (this.showPreview && msgHdr.getProperty("preview")) {
+          // Get the preview text as a UTF-8 encoded string.
+          msgPopup.querySelector(".folderSummary-previewText").textContent =
+            decodeURIComponent(escape(msgHdr.getStringProperty("preview")));
+        }
+        this.appendChild(msgPopup);
+      }
+    }
+    return foundNewMsg;
+  }
+
+  _folderSummaryMessagePopup() {
+    let vbox = document.createElement("vbox");
+    vbox.setAttribute("class", "folderSummaryMessage");
+
+    let hbox = document.createElement("hbox");
+    hbox.setAttribute("class", "folderSummary-message-row");
+
+    let subject = document.createElement("label");
+    subject.setAttribute("class", "folderSummary-subject");
+    subject.setAttribute("flex", "1");
+    subject.setAttribute("crop", "right");
+
+    let sender = document.createElement("label");
+    sender.setAttribute("class", "folderSummary-sender");
+    sender.setAttribute("crop", "right");
+
+    hbox.appendChild(subject);
+    hbox.appendChild(sender);
+
+    let preview = document.createElement("description");
+    preview.setAttribute("class", "folderSummary-message-row folderSummary-previewText");
+    preview.setAttribute("crop", "right");
+
+    vbox.appendChild(hbox);
+    vbox.appendChild(preview);
+    return vbox;
+  }
+}
+
+/**
+ * MozFolderTooltip displays a tooltip summarizing the folder status:
+ *  - if there are NEW messages, display a summary of them
+ *  - if the folder name is cropped, include the name and more details
+ *  - a summary of the unread count in this folder and its subfolders
+ */
+class MozFolderTooltip extends MozFolderSummary {
+  constructor() {
+    super();
+
+    this.showSubject = true;
+    this.showSender = true;
+    this.showPreview = true;
+  }
+
+  /** Handle the popupshowing event. */
+  folderpopupShowing(event) {
+    let msgFolder = gFolderTreeView.getFolderAtCoords(event.clientX,
+                                                      event.clientY);
+    if (!msgFolder) {
+      return false;
+    }
+
+    let tooltipnode = event.target;
+    let asyncResults = {};
+    if (tooltipnode.parseFolder(msgFolder, null, asyncResults)) {
+      return true;
+    }
+
+    let row = {}, col = {};
+    gFolderTreeView._tree.getCellAt(event.clientX, event.clientY, row, col, {});
+    if (col.value.id == "folderNameCol") {
+      let cropped = gFolderTreeView._tree.isCellCropped(row.value, col.value);
+      if (this._addLocationInfo(msgFolder, cropped, tooltipnode)) {
+        return true;
+      }
+    }
+
+    let counts = gFolderTreeView.getSummarizedCounts(row.value, col.value.id);
+    return this._addSummarizeExplain(counts, tooltipnode);
+  }
+
+  /** Handle the popuphiding event. */
+  folderpopupHiding(event) {
+    let node = event.target;
+    while (node.hasChildNodes()) {
+      node.lastChild.remove();
+    }
+  }
+
+  /** Add location information to the folder name if needed. */
+  _addLocationInfo(folder, cropped, node) {
+      // Display also server name for items that are on level 0 and are not
+      // server names by themselves and do not have server name already appended
+      // in their label.
+      let folderIndex = gFolderTreeView.getIndexOfFolder(folder);
+      if (!folder.isServer &&
+          gFolderTreeView.getLevel(folderIndex) == 0 &&
+          !gFolderTreeView.getServerNameAdded(folderIndex)) {
+        let loc = document.createElement("label");
+        let midPath = "";
+        let midFolder = folder.parent;
+        while (folder.server.rootFolder != midFolder) {
+          midPath = midFolder.name + " - " + midPath;
+          midFolder = midFolder.parent;
+        }
+        loc.setAttribute("value", folder.server.prettyName + " - " + midPath + folder.name);
+        node.appendChild(loc);
+        return true;
+      }
+
+      // If folder name is cropped or is a newsgroup and abbreviated per
+      // pref, use the full name as a tooltip.
+      if (cropped ||
+          ((folder.server instanceof Ci.nsINntpIncomingServer) &&
+           !(folder.flags & Ci.nsMsgFolderFlags.Virtual) &&
+           folder.server.abbreviate) && !folder.isServer) {
+        let loc = document.createElement("label");
+        loc.setAttribute("value", folder.name);
+        node.appendChild(loc);
+        return true;
+      }
+      return false;
+    }
+
+  /** Add information about unread messages in this folder and subfolders. */
+  _addSummarizeExplain(counts, node) {
+      if (!counts || !counts[1]) {
+        return false;
+      }
+      let expl = document.createElement("label");
+      let sumString = document.getElementById("bundle_messenger")
+        .getFormattedString("subfoldersExplanation", [counts[0], counts[1]], 2);
+      expl.setAttribute("value", sumString);
+      node.appendChild(expl);
+      return true;
+    }
+}
+
+customElements.define("folder-summary", MozFolderSummary);
+customElements.define("folder-tooltip", MozFolderTooltip);
+
--- a/mail/base/content/mailWidgets.js
+++ b/mail/base/content/mailWidgets.js
@@ -1,14 +1,18 @@
 /**
  * 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/. */
 
-/* global MozXULElement, openUILink, MessageIdClick, onClickEmailStar, onClickEmailPresence */
+/* global MozXULElement */
+/* global openUILink */
+/* global MessageIdClick */
+/* global onClickEmailStar */
+/* global onClickEmailPresence */
 
 class MozMailHeaderfield extends MozXULElement {
   connectedCallback() {
     this.setAttribute("context", "copyPopup");
     this.classList.add("headerValue");
   }
 
   set headerValue(val) {
--- a/mail/base/content/mailWidgets.xml
+++ b/mail/base/content/mailWidgets.xml
@@ -2064,314 +2064,16 @@
       <handler event="popuphiding">
         <![CDATA[
           this.parentNode.removeEventListener("blur", this.onBlurMenuList);
         ]]>
       </handler>
     </handlers>
   </binding>
 
-  <binding id="folderSummary-popup" extends="chrome://global/content/bindings/popup.xml#tooltip">
-    <content>
-      <children>
-        <xul:folderSummary/>
-      </children>
-    </content>
-    <handlers>
-      <handler event="popupshowing">
-        <![CDATA[
-          let msgFolder = gFolderTreeView.getFolderAtCoords(event.clientX,
-                                                            event.clientY);
-          if (!msgFolder)
-            return false;
-
-          let tooltipnode = document.getAnonymousNodes(this)[0];
-          let asyncResults = {};
-          if (tooltipnode.parseFolder(msgFolder, null, asyncResults))
-            return true;
-
-          let row = {}, col = {};
-          gFolderTreeView._tree.getCellAt(event.clientX, event.clientY, row, col, {});
-          if (col.value.id == "folderNameCol") {
-            let cropped = gFolderTreeView._tree.isCellCropped(row.value, col.value);
-            if (tooltipnode.addLocationInfo(msgFolder, cropped))
-              return true;
-          }
-
-          let counts = gFolderTreeView.getSummarizedCounts(row.value, col.value.id);
-          if (counts) {
-            if (tooltipnode.addSummarizeExplain(counts))
-              return true;
-          }
-
-          return false;
-        ]]>
-      </handler>
-
-      <handler event="popuphiding">
-        document.getAnonymousNodes(this)[0].clear();
-      </handler>
-    </handlers>
-  </binding>
-
-  <binding id="folderSummary">
-    <content>
-      <xul:vbox/>
-    </content>
-
-    <implementation>
-      <field name="mMaxMsgHdrsInPopup">8</field>
-      <property name="hasMessages" readonly="true" onget="return document.getAnonymousNodes(this)[0].hasChildNodes();"/>
-      <method name="parseFolder">
-        <parameter name="aFolder"/>
-        <parameter name="aUrlListener"/>
-        <parameter name="aOutAsync"/>
-        <body>
-          <![CDATA[
-            // skip servers, Trash, Junk folders and newsgroups
-            if (!aFolder || aFolder.isServer || !aFolder.hasNewMessages ||
-                aFolder.getFlag(Ci.nsMsgFolderFlags.Junk) ||
-                aFolder.getFlag(Ci.nsMsgFolderFlags.Trash) ||
-                (aFolder.server instanceof Ci.nsINntpIncomingServer))
-              return false;
-            var showPreviewText = this.Services.prefs.getBoolPref("mail.biff.alert.show_preview");
-            let folderArray = [];
-            let msgDatabase;
-            try {
-              msgDatabase = aFolder.msgDatabase;
-            } catch(e) {
-              // The database for this folder may be missing (e.g. outdated/missing .msf),
-              // so just skip this folder.
-              return false;
-            }
-
-            if (aFolder.flags & Ci.nsMsgFolderFlags.Virtual)
-            {
-              let dbFolderInfo = msgDatabase.dBFolderInfo;
-              var srchFolderUri = dbFolderInfo.getCharProperty("searchFolderUri");
-              var srchFolderUriArray = srchFolderUri.split('|');
-              var foldersAdded = 0;
-              var RDF = Cc['@mozilla.org/rdf/rdf-service;1']
-                          .getService(Ci.nsIRDFService);
-              for (var i in srchFolderUriArray)
-              {
-                var realFolder = RDF.GetResource(srchFolderUriArray[i])
-                                    .QueryInterface(Ci.nsIMsgFolder);
-                if (!realFolder.isServer)
-                  folderArray[foldersAdded++] = realFolder;
-              }
-            }
-            else {
-              folderArray[0] = aFolder;
-            }
-
-            var foundNewMsg = false;
-            for (var folderIndex = 0; folderIndex < folderArray.length; folderIndex++)
-            {
-              aFolder = folderArray[folderIndex];
-              // now get the database
-              try {
-                msgDatabase = aFolder.msgDatabase;
-              } catch(e) {
-                // The database for this folder may be missing (e.g. outdated/missing .msf),
-                // then just skip this folder.
-                continue;
-              }
-
-              aFolder.msgDatabase = null;
-              var msgKeys = {};
-              var numMsgKeys = {};
-              msgDatabase.getNewList(numMsgKeys, msgKeys);
-
-              if (!numMsgKeys.value)
-                continue;
-
-              if (showPreviewText)
-              {
-                // fetchMsgPreviewText forces the previewText property to get generated
-                // for each of the message keys.
-                try {
-                  aOutAsync.value = aFolder.fetchMsgPreviewText(msgKeys.value, numMsgKeys.value, false, aUrlListener);
-                  aFolder.msgDatabase = null;
-                }
-                catch (ex)
-                {
-                  // fetchMsgPreviewText throws an error when we call it on a news folder, we should just not show
-                  // the tooltip if this method returns an error.
-                  aFolder.msgDatabase = null;
-                  continue;
-                }
-              }
-              // if fetching the preview text is going to be an asynch operation and the caller
-              // is set up to handle that fact, then don't bother filling in any of the fields since
-              // we'll have to do this all over again when the fetch for the preview text completes.
-              // We don't expect to get called with a urlListener if we're doing a virtual folder.
-              if (aOutAsync.value && aUrlListener)
-                return false;
-              var unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-                                    .createInstance(Ci.nsIScriptableUnicodeConverter);
-              unicodeConverter.charset = "UTF-8";
-              foundNewMsg = true;
-
-              var index = 0;
-              while (document.getAnonymousNodes(this)[0].childNodes.length < this.mMaxMsgHdrsInPopup && index < numMsgKeys.value)
-              {
-                var msgPopup = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "folderSummaryMessage");
-                var msgHdr = msgDatabase.GetMsgHdrForKey(msgKeys.value[index++]);
-
-                var msgSubject = msgHdr.mime2DecodedSubject;
-                const kMsgFlagHasRe = 0x0010; // MSG_FLAG_HAS_RE
-                if(msgHdr.flags & kMsgFlagHasRe)
-                  msgSubject = (msgSubject) ? "Re: " + msgSubject : "Re: ";
-
-                msgPopup.setAttribute('subject', msgSubject);
-
-                var previewText = msgHdr.getStringProperty('preview');
-                // convert the preview text from utf-8 to unicode
-                if (previewText)
-                {
-                  try
-                  {
-                    var text = unicodeConverter.ConvertToUnicode(previewText);
-                    if (text)
-                      msgPopup.setAttribute('previewText', text);
-                  }
-                  catch (ex) { }
-                }
-
-                let addrs = MailServices.headerParser.parseEncodedHeader(
-                  msgHdr.author, msgHdr.effectiveCharset, false);
-                if (addrs.length > 0)
-                  msgPopup.setAttribute('sender', addrs[0].name || addrs[0].email);
-                msgPopup.msgHdr = msgHdr;
-                document.getAnonymousNodes(this)[0].appendChild(msgPopup);
-              }
-              if (document.getAnonymousNodes(this)[0].childNodes.length >= this.mMaxMsgHdrsInPopup)
-                return true;
-            }
-            return foundNewMsg;
-          ]]>
-        </body>
-      </method>
-
-      <method name="addLocationInfo">
-        <parameter name="aFolder"/>
-        <parameter name="aCropped"/>
-        <body>
-          <![CDATA[
-            // Display also server name for items that are on level 0 and are not server names
-            // by themselves and do not have server name already appended in their label.
-            let folderIndex = gFolderTreeView.getIndexOfFolder(aFolder);
-            if (!aFolder.isServer &&
-                gFolderTreeView.getLevel(folderIndex) == 0 &&
-                !gFolderTreeView.getServerNameAdded(folderIndex))
-            {
-              let loc = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "label");
-              let midPath = "";
-              let midFolder = aFolder.parent;
-              while (aFolder.server.rootFolder != midFolder) {
-                midPath = midFolder.name + " - " + midPath;
-                midFolder = midFolder.parent;
-              }
-              loc.setAttribute("value", aFolder.server.prettyName + " - " + midPath + aFolder.name);
-              document.getAnonymousNodes(this)[0].appendChild(loc);
-              return true;
-            }
-
-            // If folder name is cropped or is a newsgroup and abbreviated per
-            // pref, use the full name as a tooltip.
-            if (aCropped ||
-                ((aFolder.server instanceof Ci.nsINntpIncomingServer) &&
-                 !(aFolder.flags & Ci.nsMsgFolderFlags.Virtual) &&
-                 aFolder.server.abbreviate) && !aFolder.isServer) {
-              let loc = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "label");
-              loc.setAttribute("value", aFolder.name);
-              document.getAnonymousNodes(this)[0].appendChild(loc);
-              return true;
-            }
-
-            return false;
-          ]]>
-        </body>
-      </method>
-
-      <method name="addSummarizeExplain">
-        <parameter name="aCounts"/>
-        <body>
-          <![CDATA[
-            if (!aCounts || !aCounts[1])
-              return false;
-            let expl = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "label");
-            let sumString = document.getElementById("bundle_messenger")
-                                    .getFormattedString("subfoldersExplanation", [aCounts[0], aCounts[1]], 2);
-            expl.setAttribute("value", sumString);
-            document.getAnonymousNodes(this)[0].appendChild(expl);
-            return true;
-          ]]>
-        </body>
-      </method>
-
-      <method name="clear">
-        <body>
-          <![CDATA[
-            var containingBox = document.getAnonymousNodes(this)[0];
-            while (containingBox.hasChildNodes())
-              containingBox.lastChild.remove();
-          ]]>
-        </body>
-      </method>
-      <constructor>
-        <![CDATA[
-          ChromeUtils.import("resource:///modules/MailServices.jsm");
-          ChromeUtils.import("resource://gre/modules/Services.jsm", this);
-        ]]>
-      </constructor>
-    </implementation>
-  </binding>
-
-  <binding id="folderSummary-message">
-    <content>
-      <xul:vbox class="folderSummaryMessage">
-        <xul:hbox class="folderSummary-message-row">
-          <xul:label anonid="subject" flex="1" class="folderSummary-subject" xbl:inherits="value=subject" crop="right"/>
-          <xul:label anonid="sender"  class="folderSummary-sender" xbl:inherits="value=sender" crop="right"/>
-          <xul:spring anonid="spring" flex="100%"/>
-        </xul:hbox>
-        <xul:description anonid="preview" class="folderSummary-message-row folderSummary-previewText" xbl:inherits="value=previewText" crop="right"></xul:description>
-      </xul:vbox>
-    </content>
-    <implementation>
-      <constructor>
-        <![CDATA[
-          ChromeUtils.import("resource:///modules/MailUtils.jsm");
-          ChromeUtils.import("resource://gre/modules/Services.jsm", this);
-
-          if (!this.Services.prefs.getBoolPref("mail.biff.alert.show_preview"))
-            document.getAnonymousElementByAttribute(this, "anonid", "preview").hidden = true;
-          var hideSubject = !this.Services.prefs.getBoolPref("mail.biff.alert.show_subject");
-          var hideSender = !this.Services.prefs.getBoolPref("mail.biff.alert.show_sender");
-          if (hideSubject)
-            document.getAnonymousElementByAttribute(this, "anonid", "subject").hidden = true;
-          if (hideSender)
-            document.getAnonymousElementByAttribute(this, "anonid", "sender").hidden = true;
-          if (hideSubject && hideSender)
-            document.getAnonymousElementByAttribute(this, "anonid", "spring").hidden = true;
-        ]]>
-      </constructor>
-    </implementation>
-    <handlers>
-      <handler event="click" button="0">
-        <![CDATA[
-          MailUtils.displayMessageInFolderTab(this.msgHdr);
-          if (gAlertListener)
-            gAlertListener.observe(null, "alertclickcallback", "");
-        ]]>
-      </handler>
-    </handlers>
-  </binding>
   <binding id="splitmenu">
     <content>
       <xul:hbox anonid="menuitem" flex="1"
                 class="splitmenu-menuitem"
                 xbl:inherits="iconic,label,disabled,onclick=oncommand,_moz-menuactive=active"/>
       <xul:menu anonid="menu" class="splitmenu-menu"
                 xbl:inherits="disabled,_moz-menuactive=active"
                 oncommand="event.stopPropagation();">
--- a/mail/base/content/messenger.css
+++ b/mail/base/content/messenger.css
@@ -108,31 +108,16 @@ dummy.usesMailWidgets {
 .remote-gloda-search {
   -moz-binding: url("chrome://messenger/content/search.xml#glodaSearch") !important;
 }
 
 .remote-gloda-search-container {
   min-width: 10em;
 }
 
-.folderSummaryPopup
-{
-  -moz-binding: url("chrome://messenger/content/mailWidgets.xml#folderSummary-popup");
-}
-
-folderSummary
-{
-  -moz-binding: url("chrome://messenger/content/mailWidgets.xml#folderSummary");
-}
-
-folderSummaryMessage
-{
-  -moz-binding: url("chrome://messenger/content/mailWidgets.xml#folderSummary-message");
-}
-
 tabmail {
   -moz-binding: url("chrome://messenger/content/tabmail.xml#tabmail");
 }
 
 menupopup[type="folder"] {
   -moz-binding: url("chrome://messenger/content/folderWidgets.xml#folder-menupopup");
 }
 
--- a/mail/base/content/messenger.xul
+++ b/mail/base/content/messenger.xul
@@ -295,20 +295,20 @@
     </menu>
     <menuitem label="&closeTabCmd2.label;"
                   accesskey="&closeTabCmd2.accesskey;"
                   anonid="closeTab"
                   oncommand="document.getElementById('tabmail').closeTab(document.popupNode);"/>
   </menupopup>
 
   <tooltip id="tabmail-tabs-tooltip" onpopupshowing="document.getElementById('tabmail').createTooltip(event);"/>
-  <tooltip id="folderpopup" class="folderSummaryPopup"/>
+  <tooltip id="folderpopup" is="folder-tooltip" onpopupshowing="return this.folderpopupShowing(event);"
+           onpopuphiding="this.folderpopupHiding(event);"/>
 
   <tooltip id="aHTMLTooltip" page="true"/>
-  <tooltip id="tabmail-tabs-tooltip" onpopupshowing="document.getElementById('tabmail').createTooltip(event);"/>
 
   <panel id="notification-popup"
          type="arrow"
          position="after_start"
          orient="vertical"
          noautofocus="true"
          role="alert"/>
 
--- a/mail/base/jar.mn
+++ b/mail/base/jar.mn
@@ -10,16 +10,17 @@ messenger.jar:
 % override chrome://browser/content/browser-development-helpers.js chrome://messenger/content/browser-development-helpers.js
   content/messenger/browser-development-helpers.js (../../common/src/browser-development-helpers.js)
 #endif
     content/messenger/mailWindow.js                 (content/mailWindow.js)
     content/messenger/messageDisplay.js             (content/messageDisplay.js)
     content/messenger/extensions.xml                (content/extensions.xml)
     content/messenger/extensionsOverlay.css         (content/extensionsOverlay.css)
     content/messenger/folderDisplay.js              (content/folderDisplay.js)
+    content/messenger/foldersummary.js              (content/foldersummary.js)
     content/messenger/mailWindowOverlay.js          (content/mailWindowOverlay.js)
     content/messenger/mail-compacttheme.js          (content/mail-compacttheme.js)
 *   content/messenger/messageWindow.xul             (content/messageWindow.xul)
     content/messenger/messageWindow.js              (content/messageWindow.js)
     content/messenger/mailContextMenus.js           (content/mailContextMenus.js)
     content/messenger/nsContextMenu.js              (content/nsContextMenu.js)
 *   content/messenger/messenger.xul                 (content/messenger.xul)
 *   content/messenger/hiddenWindow.xul              (content/hiddenWindow.xul)
--- a/mail/themes/linux/mail/newmailalert.css
+++ b/mail/themes/linux/mail/newmailalert.css
@@ -42,16 +42,20 @@
   padding-inline-start: 5px;
   padding-inline-end: 5px;
 }
 
 #alertGroove {
   margin-inline-start: 11px;
 }
 
+folder-summary {
+  -moz-box-orient: vertical;
+}
+
 .folderSummary-message-row {
   /* This max width ends up dictating the overall width of the alert window
      because it controls how large the preview, subject and sender text can be
      before cropping kicks in */
   max-width: 450px;
   padding: 0px 5px;
 }
 
--- a/mail/themes/windows/mail/newmailalert.css
+++ b/mail/themes/windows/mail/newmailalert.css
@@ -42,16 +42,20 @@
   padding-inline-start: 5px;
   padding-inline-end: 5px;
 }
 
 #alertGroove {
   margin-inline-start: 11px;
 }
 
+folder-summary {
+  -moz-box-orient: vertical;
+}
+
 .folderSummary-message-row {
   /* This max width ends up dictating the overall width of the alert window
      because it controls how large the preview, subject and sender text can be
      before cropping kicks in */
   max-width: 450px;
   padding: 0 5px;
 }
 
--- a/mailnews/base/content/newmailalert.js
+++ b/mailnews/base/content/newmailalert.js
@@ -74,35 +74,32 @@ function urlListener(aFolder)
 urlListener.prototype =
 {
   OnStartRunningUrl: function(aUrl)
   {
   },
 
   OnStopRunningUrl: function(aUrl, aExitCode)
   {
-    var folderSummaryInfoEl = document.getElementById('folderSummaryInfo');
+    let folderSummaryInfoEl = document.getElementById("folderSummaryInfo");
     folderSummaryInfoEl.parseFolder(this.mFolder, null, {});
     gPendingPreviewFetchRequests--;
 
     // when we are done running all of our urls for fetching the preview text,
     // start the alert.
     if (!gPendingPreviewFetchRequests)
       showAlert();
   }
 }
 
 function onAlertLoad()
 {
   prefillAlertInfo();
-  // read out our initial settings from prefs.
-  try
-  {
-    gOpenTime = Services.prefs.getIntPref("alerts.totalOpenTime");
-  } catch (ex) {}
+
+  gOpenTime = Services.prefs.getIntPref("alerts.totalOpenTime");
 
   // bogus call to make sure the window is moved offscreen until we are ready for it.
   resizeAlert(true);
 
   // if we aren't waiting to fetch preview text, then go ahead and
   // start showing the alert.
   if (!gPendingPreviewFetchRequests)
     setTimeout(showAlert, 0); // let the JS thread unwind, to give layout
--- a/mailnews/base/content/newmailalert.xul
+++ b/mailnews/base/content/newmailalert.xul
@@ -10,26 +10,27 @@
 <window id="newMailAlertNotification"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         windowtype="alert:alert"
         role="alert"
         align="start"
         onload="onAlertLoad()">
 
   <stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
+  <script type="application/javascript" src="chrome://messenger/content/foldersummary.js"/>
   <script type="application/javascript" src="chrome://messenger/content/newmailalert.js"/>
 
   <stack id="alertContainer" mousethrough="always">
     <hbox id="alertBox">
       <hbox id ="alertImageBox" align="center" pack="center">
         <image id="alertImage"/>
       </hbox>
 
       <vbox id="alertTextBox">
         <label id="alertTitle"/>
         <separator id="alertGroove" class="groove"/>
-        <folderSummary id="folderSummaryInfo" mousethrough="never"/>
+        <folder-summary id="folderSummaryInfo" mousethrough="never"/>
       </vbox>
     </hbox>
 
     <toolbarbutton id="closeButton" class="close-icon" top="0" right="0" onclick="closeAlert();"/>
   </stack>
 </window>