Bug 1737730 - Enable printing of multiple messages. r=mkmelin
authorGeoff Lankow <geoff@darktrojan.net>
Tue, 19 Oct 2021 14:32:36 +1300
changeset 34147 e6607701060086a611efcb93514c695de562b2f2
parent 34146 d5d59bccfb10d87cdf8adf8638c345b51f8d6bdc
child 34148 888b50f0e689289bfc0183d09d37b4e3299a39f0
push id19309
push usergeoff@darktrojan.net
push dateWed, 27 Oct 2021 03:53:59 +0000
treeherdercomm-central@888b50f0e689 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin
bugs1737730
Bug 1737730 - Enable printing of multiple messages. r=mkmelin Differential Revision: https://phabricator.services.mozilla.com/D129458
calendar/base/content/calendar-command-controller.js
calendar/base/content/calendar-tab-panels.inc.xhtml
mail/base/content/mail3PaneWindowCommands.js
mail/base/content/messenger.xhtml
mail/base/content/printUtils.js
mail/components/addrbook/content/aboutAddressBook.js
--- a/calendar/base/content/calendar-command-controller.js
+++ b/calendar/base/content/calendar-command-controller.js
@@ -940,42 +940,11 @@ function calendarUpdateDeleteCommand(sel
 /**
  * Loads the printing template into a hidden browser then starts the printing
  * process for that browser.
  */
 async function printCalendar() {
   // Ensure the printing of this file will be detected by calPrintUtils.jsm.
   cal.print.ensureInitialized();
 
-  let printBrowser = document.getElementById("calendarPrintContent");
-  if (printBrowser.currentURI.spec == "about:blank") {
-    // The template page hasn't been loaded yet. Do that now.
-    await new Promise(resolve => {
-      // Store a strong reference to this progress listener.
-      printBrowser.progressListener = {
-        QueryInterface: ChromeUtils.generateQI([
-          "nsIWebProgressListener",
-          "nsISupportsWeakReference",
-        ]),
-
-        /** nsIWebProgressListener */
-        onStateChange(webProgress, request, stateFlags, status) {
-          if (
-            stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
-            printBrowser.currentURI.spec != "about:blank"
-          ) {
-            printBrowser.webProgress.removeProgressListener(this);
-            delete printBrowser.progressListener;
-            resolve();
-          }
-        },
-      };
-
-      printBrowser.webProgress.addProgressListener(
-        printBrowser.progressListener,
-        Ci.nsIWebProgress.NOTIFY_STATE_ALL
-      );
-      MailE10SUtils.loadURI(printBrowser, "chrome://calendar/content/printing-template.html");
-    });
-  }
-
-  PrintUtils.startPrintWindow(printBrowser.browsingContext, {});
+  await PrintUtils.loadPrintBrowser("chrome://calendar/content/printing-template.html");
+  PrintUtils.startPrintWindow(PrintUtils.printBrowser.browsingContext, {});
 }
--- a/calendar/base/content/calendar-tab-panels.inc.xhtml
+++ b/calendar/base/content/calendar-tab-panels.inc.xhtml
@@ -639,20 +639,16 @@
               </vbox>
             </hbox>
           </vbox>
         </vbox>
       </vbox>
     </hbox>
   </hbox>
 
-  <!-- Hidden browser so that we have something to print from. When printing,
-       printing-template.html is loaded here. -->
-  <browser id="calendarPrintContent" type="content" remote="true" hidden="true"/>
-
   <!-- This form is injected into the printing options page (by calendar-print.js)
        if we are printing the calendar. The script for the form is also in
        calendar-print.js and CSS in calendar-print.css. -->
   <template xmlns="http://www.w3.org/1999/xhtml" id="calendarPrintForm">
     <form id="calendar-print">
       <link rel="localization" href="calendar/calendar-print.ftl"/>
       <link rel="stylesheet" href="chrome://calendar/skin/shared/calendar-print.css"/>
 
--- a/mail/base/content/mail3PaneWindowCommands.js
+++ b/mail/base/content/mail3PaneWindowCommands.js
@@ -416,24 +416,17 @@ var DefaultController = {
           }
           if (command == "cmd_replylist" || command == "button_replylist") {
             return IsReplyListEnabled();
           }
           return true;
         }
         return false;
       case "cmd_print":
-        // We can't print messages unless they're being displayed, so enable
-        // printing only if exactly one message is selected and it is the
-        // message being displayed.
-        return (
-          gMessageDisplay.visible &&
-          gFolderDisplay.selectedCount == 1 &&
-          gFolderDisplay.selectedMessage == gMessageDisplay.displayedMessage
-        );
+        return gFolderDisplay.selectedCount >= 1;
       case "cmd_newMessage":
         return CanComposeMessages();
       case "cmd_viewAllHeader":
       case "cmd_viewNormalHeader":
         return true;
       case "cmd_markAsFlagged":
       case "button_file":
         return gFolderDisplay.selectedCount > 0;
@@ -912,18 +905,17 @@ var DefaultController = {
         return;
       case "cmd_openConversation":
       case "button_showconversation":
         gConversationOpener.openConversationForMessages(
           gFolderDisplay.selectedMessages
         );
         return;
       case "cmd_print":
-        let messagePaneBrowser = document.getElementById("messagepane");
-        PrintUtils.startPrintWindow(messagePaneBrowser.browsingContext, {});
+        PrintSelectedMessages();
         return;
       case "cmd_saveAsFile":
         MsgSaveAsFile();
         return;
       case "cmd_saveAsTemplate":
         MsgSaveAsTemplate();
         return;
       case "cmd_viewPageSource":
@@ -1536,8 +1528,47 @@ function CanDeleteFolder(folder) {
     specialFolder == "Outbox" ||
     (specialFolder == "Junk" && !CanRenameDeleteJunkMail(folder.URI))
   ) {
     return false;
   }
 
   return true;
 }
+
+/** Prints the messages selected in the thread pane. */
+async function PrintSelectedMessages() {
+  if (gFolderDisplay.selectedCount == 1) {
+    if (
+      gMessageDisplay.visible &&
+      gFolderDisplay.selectedMessage == gMessageDisplay.displayedMessage
+    ) {
+      // Use the already displayed message and print preview UI if we can.
+      let messagePaneBrowser = document.getElementById("messagepane");
+      PrintUtils.startPrintWindow(messagePaneBrowser.browsingContext, {});
+    } else {
+      // Load the only message in a hidden browser, then use the print preview UI.
+      let uri = gFolderDisplay.selectedMessageUris[0];
+      let messageService = messenger.messageServiceFromURI(uri);
+      await PrintUtils.loadPrintBrowser(messageService.getUrlForUri(uri).spec);
+      PrintUtils.startPrintWindow(PrintUtils.printBrowser.browsingContext, {});
+    }
+
+    return;
+  }
+
+  // Multiple messages. Get the printer settings, then load the messages into
+  // a hidden browser and print them one at a time.
+  let ps = PrintUtils.getPrintSettings();
+  Cc["@mozilla.org/embedcomp/printingprompt-service;1"]
+    .getService(Ci.nsIPrintingPromptService)
+    .showPrintDialog(window, ps);
+  if (ps.isCancelled) {
+    return;
+  }
+  ps.printSilent = true;
+
+  for (let uri of gFolderDisplay.selectedMessageUris) {
+    let messageService = messenger.messageServiceFromURI(uri);
+    await PrintUtils.loadPrintBrowser(messageService.getUrlForUri(uri).spec);
+    await PrintUtils.printBrowser.browsingContext.print(ps);
+  }
+}
--- a/mail/base/content/messenger.xhtml
+++ b/mail/base/content/messenger.xhtml
@@ -779,16 +779,18 @@
             </vbox>
           </hbox>
         </box> <!-- end of mailContent -->
 #include ../../components/im/content/chat-messenger.inc.xhtml
 #include ../../../calendar/base/content/calendar-tab-panels.inc.xhtml
 #include ../../../calendar/base/content/item-editing/calendar-item-panel.inc.xhtml
       </tabpanels>
      </tabbox>
+      <!-- Hidden browser used for printing documents without displaying them. -->
+      <browser id="hiddenPrintContent" type="content" remote="true" hidden="true"/>
     </tabmail>
     <vbox id="contentTab" collapsed="true">
       <vbox flex="1" class="contentTabInstance">
         <vbox id="dummycontenttoolbox" class="contentTabToolbox themeable-full">
           <hbox id="dummycontenttoolbar" class="contentTabToolbar">
             <toolbarbutton class="back-btn nav-button"
                            tooltiptext="&browseBackButton.tooltip;"
                            disabled="true"/>
--- a/mail/base/content/printUtils.js
+++ b/mail/base/content/printUtils.js
@@ -3,16 +3,17 @@
  * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 var { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 
 XPCOMUtils.defineLazyModuleGetters(this, {
+  MailE10SUtils: "resource:///modules/MailE10SUtils.jsm",
   SubDialogManager: "resource://gre/modules/SubDialog.jsm",
 });
 
 // Load PrintUtils lazily and modify it to suit.
 XPCOMUtils.defineLazyGetter(this, "PrintUtils", () => {
   let scope = {};
   Services.scriptloader.loadSubScript(
     "chrome://global/content/printUtils.js",
@@ -90,16 +91,55 @@ XPCOMUtils.defineLazyGetter(this, "Print
     // Prevent the superfluous initial load of a blank document
     // if we're going to load something other than about:blank.
     if (skipLoad) {
       b.setAttribute("nodefaultsrc", "true");
     }
 
     return b;
   };
+
+  scope.PrintUtils.__defineGetter__("printBrowser", () =>
+    document.getElementById("hiddenPrintContent")
+  );
+  scope.PrintUtils.loadPrintBrowser = async function(url) {
+    let printBrowser = this.printBrowser;
+    if (printBrowser.currentURI.spec == url) {
+      return;
+    }
+
+    // The template page hasn't been loaded yet. Do that now.
+    await new Promise(resolve => {
+      // Store a strong reference to this progress listener.
+      printBrowser.progressListener = {
+        QueryInterface: ChromeUtils.generateQI([
+          "nsIWebProgressListener",
+          "nsISupportsWeakReference",
+        ]),
+
+        /** nsIWebProgressListener */
+        onStateChange(webProgress, request, stateFlags, status) {
+          if (
+            stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+            printBrowser.currentURI.spec != "about:blank"
+          ) {
+            printBrowser.webProgress.removeProgressListener(this);
+            delete printBrowser.progressListener;
+            resolve();
+          }
+        },
+      };
+
+      printBrowser.webProgress.addProgressListener(
+        printBrowser.progressListener,
+        Ci.nsIWebProgress.NOTIFY_STATE_ALL
+      );
+      MailE10SUtils.loadURI(printBrowser, url);
+    });
+  };
   return scope.PrintUtils;
 });
 
 /**
  * The TabDialogBox supports opening window dialogs as SubDialogs on the tab and content
  * level. Both tab and content dialogs have their own separate managers.
  * Dialogs will be queued FIFO and cover the web content.
  * Dialogs are closed when the user reloads or leaves the page.
--- a/mail/components/addrbook/content/aboutAddressBook.js
+++ b/mail/components/addrbook/content/aboutAddressBook.js
@@ -2133,21 +2133,16 @@ var detailsPane = {
       )}")`;
     }
   },
 };
 
 // Printing
 
 var printHandler = {
-  QueryInterface: ChromeUtils.generateQI([
-    "nsIWebProgressListener",
-    "nsISupportsWeakReference",
-  ]),
-
   printDirectory(directory) {
     let title = directory ? directory.dirName : document.title;
 
     let cards;
     if (directory) {
       cards = directory.childCards;
     } else {
       cards = [];
@@ -2197,42 +2192,17 @@ var printHandler = {
             `</directory>`,
           ],
           "text/xml"
         )
       )
     );
   },
 
-  _printURL(url) {
-    let stack = window.browsingContext.topFrameElement.parentNode;
-    this._browser = stack.querySelector("browser.aboutAddressBookPrint");
-
-    if (!this._browser) {
-      this._browser = stack.ownerDocument.createXULElement("browser");
-      this._browser.classList.add("aboutAddressBookPrint");
-      this._browser.setAttribute("type", "content");
-      this._browser.setAttribute("hidden", "true");
-      this._browser.setAttribute("remote", "true");
-
-      stack.appendChild(this._browser);
-    }
-
-    this._browser.webProgress.addProgressListener(
-      this,
-      Ci.nsIWebProgress.NOTIFY_STATE_ALL
+  async _printURL(url) {
+    let topWindow = window.browsingContext.topChromeWindow;
+    await topWindow.PrintUtils.loadPrintBrowser(url);
+    topWindow.PrintUtils.startPrintWindow(
+      topWindow.PrintUtils.printBrowser.browsingContext,
+      {}
     );
-
-    MailE10SUtils.loadURI(this._browser, url);
-  },
-
-  /** nsIWebProgressListener */
-  onStateChange(webProgress, request, stateFlags, status) {
-    if (
-      stateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
-      this._browser.currentURI.spec != "about:blank"
-    ) {
-      let topWindow = window.browsingContext.topChromeWindow;
-      topWindow.PrintUtils.startPrintWindow(this._browser.browsingContext, {});
-      this._browser.webProgress.removeProgressListener(this);
-    }
   },
 };