Bug 1626161 - Minimize to tray on Windows. r=mkmelin
authorGeoff Lankow <geoff@darktrojan.net>
Tue, 10 Mar 2020 21:49:26 +1300
changeset 29150 d9c8e04388ebab7b11181938c188e873d360efc3
parent 29149 03cfd777e6359f5628d750985da7c4c5d5c45935
child 29151 817be4121219fd8a0d3a746d0d7e46810474baca
push id17225
push usergeoff@darktrojan.net
push dateWed, 01 Apr 2020 07:17:53 +0000
treeherdercomm-central@d9c8e04388eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin
bugs1626161
Bug 1626161 - Minimize to tray on Windows. r=mkmelin
mail/app/profile/all-thunderbird.js
mail/base/content/messenger.xhtml
mail/base/content/minimizeToTray.js
mail/base/jar.mn
mail/components/preferences/general.inc.xhtml
mail/components/preferences/general.js
mail/locales/en-US/chrome/messenger/preferences/general.dtd
mailnews/base/public/moz.build
mailnews/base/public/nsIMessengerWindowsIntegration.idl
mailnews/base/src/nsMessengerWinIntegration.cpp
mailnews/base/src/nsMessengerWinIntegration.h
--- a/mail/app/profile/all-thunderbird.js
+++ b/mail/app/profile/all-thunderbird.js
@@ -1259,8 +1259,11 @@ pref("toolkit.telemetry.bhrPing.enabled"
 
 // Required to enable telemetry pings (defaults to true if
 // MOZ_SERVICES_HEALTHREPORT is defined, but we're not yet sure we want
 // that...)
 pref("datareporting.healthreport.uploadEnabled", true);
 
 pref("toolkit.telemetry.infoURL", "https://www.mozilla.org/thunderbird/legal/privacy/#telemetry");
 
+#ifdef XP_WIN
+pref("mail.minimizeToTray", false);
+#endif
--- a/mail/base/content/messenger.xhtml
+++ b/mail/base/content/messenger.xhtml
@@ -206,16 +206,19 @@
 <script src="chrome://messenger/content/messenger-customization.js"/>
 <script src="chrome://messenger/content/customizable-toolbar.js"/>
 <!-- panelUI.js is for the appmenus. -->
 <script src="chrome://messenger/content/customizableui/panelUI.js"/>
 #ifdef XP_MACOSX
 <script src="chrome://messenger/content/macMessengerMenu.js"/>
 <script src="chrome://global/content/macWindowMenu.js"/>
 #endif
+#ifdef XP_WIN
+<script src="chrome://messenger/content/minimizeToTray.js"></script>
+#endif
 
 <!-- move needed functions into a single js file -->
 <script src="chrome://messenger/content/threadPane.js"/>
 
 <!-- calendar imip bar -->
 <script src="chrome://lightning/content/lightning-utils.js"/>
 <script src="chrome://lightning/content/imip-bar.js"/>
 <!-- calendar-management.js also needed for multiple calendar support and today pane -->
new file mode 100644
--- /dev/null
+++ b/mail/base/content/minimizeToTray.js
@@ -0,0 +1,19 @@
+/* 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 docShell, Services, windowState */
+
+addEventListener("sizemodechange", () => {
+  if (
+    windowState == window.STATE_MINIMIZED &&
+    Services.prefs.getBoolPref("mail.minimizeToTray", false)
+  ) {
+    setTimeout(() => {
+      var bw = docShell.treeOwner.QueryInterface(Ci.nsIBaseWindow);
+      Cc["@mozilla.org/messenger/osintegration;1"]
+        .getService(Ci.nsIMessengerWindowsIntegration)
+        .HideWindow(bw);
+    });
+  }
+});
--- a/mail/base/jar.mn
+++ b/mail/base/jar.mn
@@ -79,16 +79,19 @@ messenger.jar:
     content/messenger/composerOverlay.css           (content/composerOverlay.css)
     content/messenger/threadPane.js                 (content/threadPane.js)
     content/messenger/protovis-r2.6-modded.js       (content/protovis-r2.6-modded.js)
     content/messenger/newInstall.xhtml              (content/newInstall.xhtml)
     content/messenger/newInstall.js                 (content/newInstall.js)
 #ifdef XP_MACOSX
     content/messenger/macMessengerMenu.js           (content/macMessengerMenu.js)
 #endif
+#ifdef XP_WIN
+    content/messenger/minimizeToTray.js             (content/minimizeToTray.js)
+#endif
     content/messenger/selectionsummaries.js         (content/selectionsummaries.js)
     content/messenger/multimessageview.css          (content/multimessageview.css)
     content/messenger/multimessageview_print.css    (content/multimessageview_print.css)
     content/messenger/sharedsummary.css             (content/sharedsummary.css)
     content/messenger/multimessageview.xhtml        (content/multimessageview.xhtml)
     content/messenger/multimessageview.js           (content/multimessageview.js)
     content/messenger/glodaFacetTab.js              (content/glodaFacetTab.js)
     content/messenger/glodaFacetViewWrapper.xhtml   (content/glodaFacetViewWrapper.xhtml)
--- a/mail/components/preferences/general.inc.xhtml
+++ b/mail/components/preferences/general.inc.xhtml
@@ -83,16 +83,23 @@
                   label="&alwaysCheckDefault.label;"
                   accesskey="&alwaysCheckDefault.accesskey;"/>
         <spacer flex="1"/>
         <button id="checkDefaultButton" label="&checkDefaultsNow.label;"
                 accesskey="&checkDefaultsNow.accesskey;"
                 oncommand="gGeneralPane.checkDefaultNow();"
                 preference="pref.general.disable_button.default_mail"/>
       </hbox>
+#ifdef XP_WIN
+      <hbox align="start">
+        <checkbox label="&minimizeToTray.label;"
+                  accesskey="&minimizeToTray.accesskey;"
+                  preference="mail.minimizeToTray"/>
+      </hbox>
+#endif
       <hbox id="searchIntegrationContainer">
         <checkbox id="searchIntegration"
                   preference="searchintegration.enable"
                   label="&searchIntegration.label;"
                   accesskey="&searchIntegration.accesskey;"/>
       </hbox>
       </vbox>
     </html:fieldset>
--- a/mail/components/preferences/general.js
+++ b/mail/components/preferences/general.js
@@ -86,16 +86,19 @@ Preferences.addAll([
   { id: "mail.close_message_window.on_delete", type: "bool" },
   { id: "mail.prompt_purge_threshhold", type: "bool" },
   { id: "mail.purge_threshhold_mb", type: "int" },
   { id: "browser.cache.disk.capacity", type: "int" },
   { id: "browser.cache.disk.smart_size.enabled", inverted: true, type: "bool" },
   { id: "layers.acceleration.disabled", type: "bool", inverted: true },
   { id: "searchintegration.enable", type: "bool" },
 ]);
+if (AppConstants.platform == "win") {
+  Preferences.add({ id: "mail.minimizeToTray", type: "bool" });
+}
 if (AppConstants.platform != "macosx") {
   Preferences.add({ id: "mail.biff.show_alert", type: "bool" });
 }
 
 var ICON_URL_APP = "";
 
 if (AppConstants.MOZ_WIDGET_GTK) {
   ICON_URL_APP = "moz-icon://dummy.exe?size=16";
--- a/mail/locales/en-US/chrome/messenger/preferences/general.dtd
+++ b/mail/locales/en-US/chrome/messenger/preferences/general.dtd
@@ -37,8 +37,11 @@
 <!ENTITY browse.label                     "Browse…">
 <!ENTITY browse.accesskey                 "B">
 
 <!ENTITY defaultSearchEngine.label        "Default Search Engine">
 <!ENTITY addSearchEngine.label            "Add from file">
 <!ENTITY addSearchEngine.accesskey        "A">
 <!ENTITY removeSearchEngine.label         "Remove">
 <!ENTITY removeSearchEngine.accesskey     "v">
+
+<!ENTITY minimizeToTray.label             "When &brandShortName; is minimized, move it to the tray">
+<!ENTITY minimizeToTray.accesskey         "m">
--- a/mailnews/base/public/moz.build
+++ b/mailnews/base/public/moz.build
@@ -58,16 +58,21 @@ XPIDL_SOURCES += [
     'nsIStatusBarBiffManager.idl',
     'nsIStopwatch.idl',
     'nsISubscribableServer.idl',
     'nsIUrlListener.idl',
     'nsMsgFolderFlags.idl',
     'nsMsgMessageFlags.idl',
 ]
 
+if CONFIG['OS_ARCH'] == 'WINNT':
+    XPIDL_SOURCES += [
+        'nsIMessengerWindowsIntegration.idl',
+    ]
+
 XPIDL_MODULE = 'msgbase'
 
 EXPORTS += [
     'MailNewsTypes.h',
     'msgCore.h',
     'nsMsgBaseCID.h',
     'nsMsgHeaderMasks.h',
     'nsMsgLocalFolderHdrs.h',
new file mode 100644
--- /dev/null
+++ b/mailnews/base/public/nsIMessengerWindowsIntegration.idl
@@ -0,0 +1,12 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* 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/. */
+
+#include "nsIBaseWindow.idl"
+#include "nsIMessengerOSIntegration.idl"
+
+[scriptable, uuid(e14eb9fe-e05e-4b78-bd31-5b7e1497f91b)]
+interface nsIMessengerWindowsIntegration : nsIMessengerOSIntegration {
+  void HideWindow(in nsIBaseWindow aWindow);
+};
--- a/mailnews/base/src/nsMessengerWinIntegration.cpp
+++ b/mailnews/base/src/nsMessengerWinIntegration.cpp
@@ -246,16 +246,17 @@ nsMessengerWinIntegration::~nsMessengerW
   DestroyBiffIcon();
 }
 
 NS_IMPL_ADDREF(nsMessengerWinIntegration)
 NS_IMPL_RELEASE(nsMessengerWinIntegration)
 
 NS_INTERFACE_MAP_BEGIN(nsMessengerWinIntegration)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMessengerOSIntegration)
+  NS_INTERFACE_MAP_ENTRY(nsIMessengerWindowsIntegration)
   NS_INTERFACE_MAP_ENTRY(nsIMessengerOSIntegration)
   NS_INTERFACE_MAP_ENTRY(nsIFolderListener)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
 
 nsresult nsMessengerWinIntegration::ResetCurrent() {
   mInboxURI.Truncate();
   mEmail.Truncate();
@@ -1055,8 +1056,98 @@ nsresult nsMessengerWinIntegration::Setu
 
   mUnreadCountUpdateTimer->InitWithNamedFuncCallback(
       OnUnreadCountUpdateTimer, (void *)this, UNREAD_UPDATE_INTERVAL,
       nsITimer::TYPE_ONE_SHOT,
       "nsMessengerWinIntegration::OnUnreadCountUpdateTimer");
 
   return NS_OK;
 }
+
+NOTIFYICONDATAW sMailIconData = {
+  /* cbSize */ (DWORD)NOTIFYICONDATAW_V2_SIZE,
+  /* hWnd */ 0,
+  /* uID */ 2,
+  /* uFlags */ NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO,
+  /* uCallbackMessage */ WM_USER,
+  /* hIcon */ 0,
+  /* szTip */ L"",
+  /* dwState */ 0,
+  /* dwStateMask */ 0,
+  /* szInfo */ L"",
+  /* uVersion */ {30000},
+  /* szInfoTitle */ L"",
+  /* dwInfoFlags */ NIIF_USER | NIIF_NOSOUND
+};
+
+static nsCOMArray<nsIBaseWindow> sHiddenWindows;
+static HWND sIconWindow;
+static LRESULT CALLBACK IconWindowProc(HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp) {
+  if (msg == WM_USER && lp == WM_LBUTTONDOWN) {
+    ::Shell_NotifyIconW(NIM_DELETE, &sMailIconData);
+
+    uint32_t count = sHiddenWindows.Length();
+    for (uint32_t i = 0; i < count; i++) {
+      sHiddenWindows[i]->SetVisibility(true);
+
+      nsCOMPtr<nsIWidget> widget;
+      sHiddenWindows[i]->GetMainWidget(getter_AddRefs(widget));
+
+      HWND hwnd = (HWND)(widget->GetNativeData(NS_NATIVE_WIDGET));
+      ::ShowWindow(hwnd, SW_RESTORE);
+      ::SetForegroundWindow(hwnd);
+    }
+
+    sHiddenWindows.Clear();
+  }
+  return TRUE;
+}
+
+WNDCLASS sClassStruct = {
+  /* style */ 0,
+  /* lpfnWndProc */ &IconWindowProc,
+  /* cbClsExtra */ 0,
+  /* cbWndExtra */ 0,
+  /* hInstance */ 0,
+  /* hIcon */ 0,
+  /* hCursor */ 0,
+  /* hbrBackground */ 0,
+  /* lpszMenuName */ 0,
+  /* lpszClassName */ L"IconWindowClass"
+};
+
+nsresult nsMessengerWinIntegration::HideWindow(nsIBaseWindow* aWindow) {
+  aWindow->SetVisibility(false);
+  sHiddenWindows.AppendElement(aWindow);
+
+  if (sMailIconData.hWnd == 0) {
+    // Register the window class.
+    NS_ENSURE_TRUE(::RegisterClass(&sClassStruct), NS_ERROR_FAILURE);
+    // Create the window.
+    NS_ENSURE_TRUE(
+      sIconWindow = ::CreateWindow(
+        /* className */ L"IconWindowClass",
+        /* title */ 0,
+        /* style */ WS_CAPTION,
+        /* x, y, cx, cy */ 0, 0, 0, 0,
+        /* parent */ 0,
+        /* menu */ 0,
+        /* instance */ 0,
+        /* create struct */ 0
+      ),
+      NS_ERROR_FAILURE
+    );
+    sMailIconData.hWnd = sIconWindow;
+    sMailIconData.hIcon = ::LoadIcon(::GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_APPLICATION));
+
+    nsCOMPtr<nsIStringBundleService> bundleService = mozilla::services::GetStringBundleService();
+    NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+    nsCOMPtr<nsIStringBundle> bundle;
+    bundleService->CreateBundle("chrome://branding/locale/brand.properties", getter_AddRefs(bundle));
+    nsString brandShortName;
+    bundle->GetStringFromName("brandShortName", brandShortName);
+    ::wcsncpy(sMailIconData.szTip, brandShortName.get(), brandShortName.Length());
+  }
+
+  ::Shell_NotifyIconW(NIM_ADD, &sMailIconData);
+  ::Shell_NotifyIconW(NIM_SETVERSION, &sMailIconData);
+  return NS_OK;
+}
--- a/mailnews/base/src/nsMessengerWinIntegration.h
+++ b/mailnews/base/src/nsMessengerWinIntegration.h
@@ -6,42 +6,42 @@
 #ifndef __nsMessengerWinIntegration_h
 #define __nsMessengerWinIntegration_h
 
 #include <windows.h>
 
 // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
 #include <shellapi.h>
 
-#include "nsIMessengerOSIntegration.h"
+#include "nsIMessengerWindowsIntegration.h"
 #include "nsIFolderListener.h"
 #include "nsITimer.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsIMutableArray.h"
 #include "nsIObserver.h"
 
 #define NS_MESSENGERWININTEGRATION_CID               \
   {                                                  \
     0xf62f3d3a, 0x1dd1, 0x11b2, {                    \
       0xa5, 0x16, 0xef, 0xad, 0xb1, 0x31, 0x61, 0x5c \
     }                                                \
   }
 
 class nsIStringBundle;
 
-class nsMessengerWinIntegration : public nsIMessengerOSIntegration,
+class nsMessengerWinIntegration : public nsIMessengerWindowsIntegration,
                                   public nsIFolderListener,
                                   public nsIObserver {
  public:
   nsMessengerWinIntegration();
   virtual nsresult Init();
 
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIMESSENGEROSINTEGRATION
+  NS_DECL_NSIMESSENGERWINDOWSINTEGRATION
   NS_DECL_NSIFOLDERLISTENER
   NS_DECL_NSIOBSERVER
 
   nsresult ShowNewAlertNotification(bool aUserInitiated,
                                     const nsString& aAlertTitle,
                                     const nsString& aAlertText);
 #ifndef MOZ_THUNDERBIRD
   nsresult ShowAlertMessage(const nsString& aAlertTitle,