Bug 1155505 - Use native Window Toasts as a notification backend draft
authorEdouard Oger <eoger@fastmail.com>
Fri, 23 Mar 2018 16:41:58 -0400
changeset 771752 7aadebd9fc1943cc934d5610c66ce04bc69185ee
parent 771261 8bf380faae74e4921be6000496ca09d4a2c44e8d
push id103779
push userbmo:eoger@fastmail.com
push dateFri, 23 Mar 2018 20:42:27 +0000
bugs1155505
milestone61.0a1
Bug 1155505 - Use native Window Toasts as a notification backend MozReview-Commit-ID: 28UVzUapdwq
browser/components/shell/nsWindowsShellService.cpp
toolkit/library/moz.build
widget/windows/ToastNotification.cpp
widget/windows/ToastNotification.h
widget/windows/ToastNotificationHandler.cpp
widget/windows/ToastNotificationHandler.h
widget/windows/WinUtils.cpp
widget/windows/WinUtils.h
widget/windows/moz.build
widget/windows/nsWidgetFactory.cpp
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -4,22 +4,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsWindowsShellService.h"
 
 #include "BinaryPath.h"
 #include "city.h"
 #include "imgIContainer.h"
 #include "imgIRequest.h"
-#include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsIContent.h"
 #include "nsIDOMElement.h"
 #include "nsIImageLoadingContent.h"
-#include "nsIOutputStream.h"
 #include "nsIPrefService.h"
 #include "nsIPrefLocalizedString.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsShellService.h"
 #include "nsIProcess.h"
@@ -41,16 +39,17 @@
 #undef _WIN32_WINNT
 #endif
 #define _WIN32_WINNT 0x0600
 #define INITGUID
 #undef NTDDI_VERSION
 #define NTDDI_VERSION NTDDI_WIN8
 // Needed for access to IApplicationActivationManager
 #include <shlobj.h>
+#include "WinUtils.h"
 
 #include <mbstring.h>
 #include <shlwapi.h>
 
 #include <lm.h>
 #undef ACCESS_READ
 
 #ifndef MAX_BUF
@@ -62,17 +61,17 @@
 
 #define REG_FAILED(val) \
   (val != ERROR_SUCCESS)
 
 #define APP_REG_NAME_BASE L"Firefox-"
 
 using mozilla::IsWin8OrLater;
 using namespace mozilla;
-using namespace mozilla::gfx;
+using namespace mozilla::widget;
 
 NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIShellService)
 
 static nsresult
 OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
 {
   const nsString &flatName = PromiseFlatString(aKeyName);
 
@@ -480,105 +479,16 @@ nsWindowsShellService::SetDefaultBrowser
     // Reset the number of times the dialog should be shown
     // before it is silenced.
     (void) prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0);
   }
 
   return rv;
 }
 
-static nsresult
-WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
-{
-  nsresult rv;
-
-  RefPtr<SourceSurface> surface =
-    aImage->GetFrame(imgIContainer::FRAME_FIRST,
-                     imgIContainer::FLAG_SYNC_DECODE);
-  NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
-
-  // For either of the following formats we want to set the biBitCount member
-  // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
-  // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
-  // for the BI_RGB value we use for the biCompression member.
-  MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
-             surface->GetFormat() == SurfaceFormat::B8G8R8X8);
-
-  RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
-  NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
-
-  int32_t width = dataSurface->GetSize().width;
-  int32_t height = dataSurface->GetSize().height;
-  int32_t bytesPerPixel = 4 * sizeof(uint8_t);
-  uint32_t bytesPerRow = bytesPerPixel * width;
-
-  // initialize these bitmap structs which we will later
-  // serialize directly to the head of the bitmap file
-  BITMAPINFOHEADER bmi;
-  bmi.biSize = sizeof(BITMAPINFOHEADER);
-  bmi.biWidth = width;
-  bmi.biHeight = height;
-  bmi.biPlanes = 1;
-  bmi.biBitCount = (WORD)bytesPerPixel*8;
-  bmi.biCompression = BI_RGB;
-  bmi.biSizeImage = bytesPerRow * height;
-  bmi.biXPelsPerMeter = 0;
-  bmi.biYPelsPerMeter = 0;
-  bmi.biClrUsed = 0;
-  bmi.biClrImportant = 0;
-
-  BITMAPFILEHEADER bf;
-  bf.bfType = 0x4D42; // 'BM'
-  bf.bfReserved1 = 0;
-  bf.bfReserved2 = 0;
-  bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
-  bf.bfSize = bf.bfOffBits + bmi.biSizeImage;
-
-  // get a file output stream
-  nsCOMPtr<nsIOutputStream> stream;
-  rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  DataSourceSurface::MappedSurface map;
-  if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // write the bitmap headers and rgb pixel data to the file
-  rv = NS_ERROR_FAILURE;
-  if (stream) {
-    uint32_t written;
-    stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
-    if (written == sizeof(BITMAPFILEHEADER)) {
-      stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written);
-      if (written == sizeof(BITMAPINFOHEADER)) {
-        // write out the image data backwards because the desktop won't
-        // show bitmaps with negative heights for top-to-bottom
-        uint32_t i = map.mStride * height;
-        do {
-          i -= map.mStride;
-          stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
-          if (written == bytesPerRow) {
-            rv = NS_OK;
-          } else {
-            rv = NS_ERROR_FAILURE;
-            break;
-          }
-        } while (i != 0);
-      }
-    }
-
-    stream->Close();
-  }
-
-  dataSurface->Unmap();
-
-  return rv;
-}
-
 NS_IMETHODIMP
 nsWindowsShellService::SetDesktopBackground(nsIDOMElement* aElement,
                                             int32_t aPosition,
                                             const nsACString& aImageName)
 {
   nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
   if (!content || !content->IsHTMLElement(nsGkAtoms::img)) {
     // XXX write background loading stuff!
@@ -629,17 +539,17 @@ nsWindowsShellService::SetDesktopBackgro
   rv = file->Append(fileLeafName);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoString path;
   rv = file->GetPath(path);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // write the bitmap to a file in the profile directory
-  rv = WriteBitmap(file, container);
+  rv = WinUtils::WriteBitmap(file, container);
 
   // if the file was written successfully, set it as the system wallpaper
   if (NS_SUCCEEDED(rv)) {
     nsCOMPtr<nsIWindowsRegKey> regKey =
       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -320,16 +320,17 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'secur32',
         'sensorsapi',
         'portabledeviceguids',
         'wininet',
         'wbemuuid',
         'wintrust',
         'wtsapi32',
         'locationapi',
+        'runtimeobject',
         'sapi',
         'dxguid',
     ]
     if CONFIG['ACCESSIBILITY']:
         OS_LIBS += [
             'oleacc',
         ]
 
new file mode 100644
--- /dev/null
+++ b/widget/windows/ToastNotification.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sts=2 sw=2 et cin: */
+/* 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 "ToastNotification.h"
+
+#include "mozilla/WindowsVersion.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIObserverService.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+
+#include "ToastNotificationHandler.h"
+
+namespace mozilla {
+namespace widget {
+
+NS_IMPL_ISUPPORTS(ToastNotification, nsIAlertsService, nsIObserver, nsISupportsWeakReference)
+
+ToastNotification::ToastNotification()
+= default;
+
+ToastNotification::~ToastNotification()
+= default;
+
+nsresult
+ToastNotification::Init()
+{
+  if (!IsWin8OrLater()) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  nsresult rv = NS_NewNamedThread("ToastBgThread", getter_AddRefs(mBackgroundThread));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIObserverService> obsServ =
+      do_GetService("@mozilla.org/observer-service;1");
+  if (obsServ) {
+    obsServ->AddObserver(this, "quit-application", true);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+ToastNotification::BackgroundDispatch(nsIRunnable* runnable)
+{
+  return mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+}
+
+NS_IMETHODIMP
+ToastNotification::Observe(nsISupports *aSubject, const char *aTopic,
+                           const char16_t *aData) {
+  // Got quit-application
+  // The handlers destructors will do the right thing (de-register with Windows).
+  mActiveHandlers.Clear();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ToastNotification::ShowAlertNotification(const nsAString & aImageUrl,
+                                         const nsAString & aAlertTitle,
+                                         const nsAString & aAlertText,
+                                         bool aAlertTextClickable,
+                                         const nsAString & aAlertCookie,
+                                         nsIObserver * aAlertListener,
+                                         const nsAString & aAlertName,
+                                         const nsAString & aBidi,
+                                         const nsAString & aLang,
+                                         const nsAString & aData,
+                                         nsIPrincipal * aPrincipal,
+                                         bool aInPrivateBrowsing,
+                                         bool aRequireInteraction)
+{
+  nsCOMPtr<nsIAlertNotification> alert =
+    do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
+  if (NS_WARN_IF(!alert)) {
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle,
+                            aAlertText, aAlertTextClickable,
+                            aAlertCookie, aBidi, aLang, aData,
+                            aPrincipal, aInPrivateBrowsing,
+                            aRequireInteraction);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return ShowAlert(alert, aAlertListener);
+}
+
+NS_IMETHODIMP
+ToastNotification::ShowPersistentNotification(const nsAString& aPersistentData,
+                                              nsIAlertNotification* aAlert,
+                                              nsIObserver* aAlertListener)
+{
+  return ShowAlert(aAlert, aAlertListener);
+}
+
+NS_IMETHODIMP
+ToastNotification::ShowAlert(nsIAlertNotification* aAlert,
+                             nsIObserver* aAlertListener)
+{
+  if (NS_WARN_IF(!aAlert)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsAutoString cookie;
+  nsresult rv = aAlert->GetCookie(cookie);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoString name;
+  rv = aAlert->GetName(name);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoString title;
+  rv = aAlert->GetTitle(title);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoString text;
+  rv = aAlert->GetText(text);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool textClickable;
+  rv = aAlert->GetTextClickable(&textClickable);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  RefPtr<ToastNotificationHandler> handler =
+    new ToastNotificationHandler(this, aAlertListener, name, cookie, title,
+                                 text, textClickable);
+  mActiveHandlers.Put(name, handler);
+
+  rv = handler->InitAlertAsync(aAlert);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mActiveHandlers.Remove(name);
+    return rv;
+  }
+
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ToastNotification::CloseAlert(const nsAString& aAlertName,
+                              nsIPrincipal* aPrincipal)
+{
+  RefPtr<ToastNotificationHandler> handler;
+  if (NS_WARN_IF(!mActiveHandlers.Get(aAlertName, getter_AddRefs(handler)))) {
+    return NS_OK;
+  }
+  mActiveHandlers.Remove(aAlertName);
+  return NS_OK;
+}
+
+bool
+ToastNotification::IsActiveHandler(const nsAString& aAlertName,
+                                   ToastNotificationHandler* aHandler)
+{
+  RefPtr<ToastNotificationHandler> handler;
+  if (NS_WARN_IF(!mActiveHandlers.Get(aAlertName, getter_AddRefs(handler)))) {
+    return false;
+  }
+  return handler == aHandler;
+}
+
+void
+ToastNotification::RemoveHandler(const nsAString& aAlertName,
+                                 ToastNotificationHandler* aHandler)
+{
+  // The alert may have been replaced; only remove it from the active
+  // handlers map if it's the same.
+  if (IsActiveHandler(aAlertName, aHandler)) {
+    // Terrible things happen if the destructor of a handler is called inside
+    // the hashtable .Remove() method. Wait until we have returned from there.
+    RefPtr<ToastNotificationHandler> kungFuDeathGrip(aHandler);
+    mActiveHandlers.Remove(aAlertName);
+  }
+}
+
+} // namespace widget
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/windows/ToastNotification.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef ToastNotification_h__
+#define ToastNotification_h__
+
+#include "nsIAlertsService.h"
+#include "nsIObserver.h"
+#include "nsIThread.h"
+#include "nsRefPtrHashtable.h"
+#include "nsWeakReference.h"
+
+namespace mozilla {
+namespace widget {
+
+class ToastNotificationHandler;
+
+class ToastNotification final : public nsIAlertsService,
+                                public nsIObserver,
+                                public nsSupportsWeakReference
+{
+public:
+  NS_DECL_NSIALERTSSERVICE
+  NS_DECL_NSIOBSERVER
+  NS_DECL_ISUPPORTS
+
+  ToastNotification();
+
+  nsresult Init();
+
+  bool IsActiveHandler(const nsAString& aAlertName,
+                       ToastNotificationHandler* aHandler);
+  void RemoveHandler(const nsAString& aAlertName,
+                     ToastNotificationHandler* aHandler);
+
+  nsresult BackgroundDispatch(nsIRunnable* runnable);
+
+protected:
+  virtual ~ToastNotification();
+
+  nsRefPtrHashtable<nsStringHashKey, ToastNotificationHandler> mActiveHandlers;
+
+  nsCOMPtr<nsIThread> mBackgroundThread;
+};
+
+} // widget
+} // mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/widget/windows/ToastNotificationHandler.cpp
@@ -0,0 +1,510 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sts=2 sw=2 et cin: */
+/* 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 "ToastNotificationHandler.h"
+
+#include "imgIRequest.h"
+#include "mozilla/gfx/2D.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIURI.h"
+#include "nsIUUIDGenerator.h"
+#include "nsNetUtil.h"
+#include "nsProxyRelease.h"
+#include "WinTaskbar.h"
+#include "WinUtils.h"
+
+#include "ToastNotification.h"
+
+namespace mozilla {
+namespace widget {
+
+typedef ABI::Windows::Foundation::ITypedEventHandler<
+  ABI::Windows::UI::Notifications::ToastNotification*, IInspectable*>
+  ToastActivationHandler;
+typedef ABI::Windows::Foundation::ITypedEventHandler<
+  ABI::Windows::UI::Notifications::ToastNotification*,
+  ABI::Windows::UI::Notifications::ToastDismissedEventArgs*> ToastDismissedHandler;
+typedef ABI::Windows::Foundation::ITypedEventHandler<
+  ABI::Windows::UI::Notifications::ToastNotification*,
+  ABI::Windows::UI::Notifications::ToastFailedEventArgs*> ToastFailedHandler;
+
+using namespace ABI::Windows::Data::Xml::Dom;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::UI::Notifications;
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(ToastNotificationHandler, nsIAlertNotificationImageListener)
+
+static bool
+SetNodeValueString(const nsAString& aString,
+                   IXmlNode* node,
+                   IXmlDocument* xml)
+{
+  ComPtr<IXmlText> inputText;
+  HRESULT hr;
+  HSTRING value = HStringReference(reinterpret_cast<const wchar_t*>(aString.BeginReading())).Get();
+
+  hr = xml->CreateTextNode(value, &inputText);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+  ComPtr<IXmlNode> inputTextNode;
+  hr = inputText.As(&inputTextNode);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+  ComPtr<IXmlNode> appendedChild;
+  hr = node->AppendChild(inputTextNode.Get(), &appendedChild);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  return true;
+}
+
+static bool
+SetAttribute(IXmlElement* element, const nsAString& name, const nsAString& value) {
+  HSTRING nameStr = HStringReference(reinterpret_cast<const wchar_t*>(name.BeginReading())).Get();
+  HSTRING valueStr = HStringReference(reinterpret_cast<const wchar_t*>(value.BeginReading())).Get();
+  if (NS_WARN_IF(FAILED(element->SetAttribute(nameStr, valueStr)))) {
+    return false;
+  }
+  return true;
+}
+
+static bool
+AddActionNode(IXmlDocument* toastXml, IXmlNode* actionsNode, const nsAString& actionTitle, const nsAString& actionArgs) {
+  ComPtr<IXmlElement> action;
+  toastXml->CreateElement(HStringReference(L"action").Get(), &action);
+
+  HRESULT hr = SetAttribute(action.Get(), NS_LITERAL_STRING("content"), actionTitle);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+  hr = SetAttribute(action.Get(), NS_LITERAL_STRING("arguments"), actionArgs);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+  hr = SetAttribute(action.Get(), NS_LITERAL_STRING("placement"), NS_LITERAL_STRING("contextmenu"));
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  // Add <action> to <actions>
+  ComPtr<IXmlNode> actionNode;
+  hr = action.As(&actionNode);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  ComPtr<IXmlNode> appendedChild;
+  hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  return true;
+}
+
+static ComPtr<IToastNotificationManagerStatics>
+GetToastNotificationManagerStatics()
+{
+  ComPtr<IToastNotificationManagerStatics> toastNotificationManagerStatics;
+  HRESULT hr;
+  hr = GetActivationFactory(
+         HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(),
+         toastNotificationManagerStatics.GetAddressOf());
+  if (NS_WARN_IF(FAILED(hr))) {
+    return nullptr;
+  }
+
+  return toastNotificationManagerStatics;
+}
+
+ToastNotificationHandler::~ToastNotificationHandler()
+{
+  if (mImageRequest) {
+    mImageRequest->Cancel(NS_BINDING_ABORTED);
+    mImageRequest = nullptr;
+  }
+
+  if (mHasImage) {
+    mImageFile->Remove(false);
+  }
+
+  if (mNotification && mNotifier) {
+    mNotification->remove_Dismissed(mDismissedToken);
+    mNotification->remove_Activated(mActivatedToken);
+    mNotification->remove_Failed(mFailedToken);
+    mNotifier->Hide(mNotification.Get());
+  }
+}
+
+ComPtr<IXmlDocument>
+ToastNotificationHandler::InitializeXmlForTemplate(
+  ToastTemplateType templateType)
+{
+  ComPtr<IToastNotificationManagerStatics> toastNotificationManagerStatics =
+    GetToastNotificationManagerStatics();
+
+  ComPtr<IXmlDocument> toastXml;
+  toastNotificationManagerStatics->GetTemplateContent(templateType, &toastXml);
+
+  return toastXml;
+}
+
+nsresult
+ToastNotificationHandler::InitAlertAsync(nsIAlertNotification* aAlert)
+{
+  return aAlert->LoadImage(/* aTimeout = */ 0, this, /* aUserData = */ nullptr,
+                           getter_AddRefs(mImageRequest));
+}
+
+bool
+ToastNotificationHandler::ShowAlert()
+{
+  if (!mBackend->IsActiveHandler(mName, this)) {
+    return true;
+  }
+
+  ComPtr<IXmlDocument> toastXml =
+    InitializeXmlForTemplate(
+      !mHasImage ?
+        ToastTemplateType::ToastTemplateType_ToastText03 :
+        ToastTemplateType::ToastTemplateType_ToastImageAndText03);
+
+  if (!toastXml) {
+    return false;
+  }
+
+  HRESULT hr;
+
+  if (mHasImage) {
+    ComPtr<IXmlNodeList> toastImageElements;
+    hr = toastXml->GetElementsByTagName(HStringReference(L"image").Get(),
+                                        toastImageElements.GetAddressOf());
+    if (NS_WARN_IF(FAILED(hr))) {
+      return false;
+    }
+    ComPtr<IXmlNode> imageNode;
+    hr = toastImageElements->Item(0, &imageNode);
+    if (NS_WARN_IF(FAILED(hr))) {
+      return false;
+    }
+    ComPtr<IXmlElement> image;
+    hr = imageNode.As(&image);
+    if (NS_WARN_IF(FAILED(hr))) {
+      return false;
+    }
+    hr = SetAttribute(image.Get(), NS_LITERAL_STRING("src"), mImageUri);
+    if (NS_WARN_IF(FAILED(hr))) {
+      return false;
+    }
+  }
+
+  ComPtr<IXmlNodeList> toastTextElements;
+  hr = toastXml->GetElementsByTagName(HStringReference(L"text").Get(),
+                                      toastTextElements.GetAddressOf());
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  ComPtr<IXmlNode> titleTextNodeRoot;
+  hr = toastTextElements->Item(0, &titleTextNodeRoot);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+  ComPtr<IXmlNode> msgTextNodeRoot;
+  hr = toastTextElements->Item(1, &msgTextNodeRoot);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  SetNodeValueString(mTitle, titleTextNodeRoot.Get(), toastXml.Get());
+  SetNodeValueString(mMsg, msgTextNodeRoot.Get(), toastXml.Get());
+
+  ComPtr<IXmlNodeList> toastElements;
+  hr = toastXml->GetElementsByTagName(HStringReference(L"toast").Get(),
+                                      toastElements.GetAddressOf());
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  ComPtr<IXmlNode> toastNodeRoot;
+  hr = toastElements->Item(0, &toastNodeRoot);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  ComPtr<IXmlElement> actions;
+  toastXml->CreateElement(HStringReference(L"actions").Get(), &actions);
+
+  ComPtr<IXmlNode> actionsNode;
+  hr = actions.As(&actionsNode);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  AddActionNode(toastXml.Get(), actionsNode.Get(), NS_LITERAL_STRING("Snooze"), NS_LITERAL_STRING("snooze=1"));
+  AddActionNode(toastXml.Get(), actionsNode.Get(), NS_LITERAL_STRING("Settings"), NS_LITERAL_STRING("settings=1"));
+
+  ComPtr<IXmlNode> appendedChild;
+  hr = toastNodeRoot->AppendChild(actionsNode.Get(), &appendedChild);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  return CreateWindowsNotificationFromXml(toastXml.Get());
+}
+
+bool
+ToastNotificationHandler::CreateWindowsNotificationFromXml(
+  IXmlDocument *aXml)
+{
+  ComPtr<IToastNotificationFactory> factory;
+  HRESULT hr;
+  hr = GetActivationFactory(
+         HStringReference(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(),
+         factory.GetAddressOf());
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  hr = factory->CreateToastNotification(aXml, mNotification.GetAddressOf());
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  hr = mNotification->add_Activated(
+         Callback<ToastActivationHandler>(
+           this, &ToastNotificationHandler::OnActivate).Get(),
+         &mActivatedToken);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  hr = mNotification->add_Dismissed(
+         Callback<ToastDismissedHandler>(
+           this, &ToastNotificationHandler::OnDismiss).Get(),
+         &mDismissedToken);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  hr = mNotification->add_Failed(
+         Callback<ToastFailedHandler>(
+           this, &ToastNotificationHandler::OnFail).Get(),
+         &mFailedToken);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  ComPtr<IToastNotificationManagerStatics> toastNotificationManagerStatics =
+    GetToastNotificationManagerStatics();
+  if (!toastNotificationManagerStatics) {
+    return false;
+  }
+
+  nsAutoString uid;
+  if (!WinTaskbar::GetAppUserModelID(uid)) {
+    return false;
+  }
+
+  HSTRING uidStr = HStringReference(reinterpret_cast<const wchar_t*>(uid.BeginReading())).Get();
+  hr = toastNotificationManagerStatics->CreateToastNotifierWithId(uidStr, &mNotifier);
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  hr = mNotifier->Show(mNotification.Get());
+  if (NS_WARN_IF(FAILED(hr))) {
+    return false;
+  }
+
+  if (mAlertListener) {
+    mAlertListener->Observe(nullptr, "alertshow", mCookie.get());
+  }
+
+  return true;
+}
+
+HRESULT
+ToastNotificationHandler::OnActivate(IToastNotification *notification,
+                                     IInspectable *inspectable)
+{
+  ComPtr<IToastActivatedEventArgs> eventArgs;
+  inspectable->QueryInterface(__uuidof(IToastActivatedEventArgs), (void **)&eventArgs);
+
+  HSTRING args;
+  eventArgs->get_Arguments(&args);
+
+  if (mClickable && mAlertListener) {
+    mAlertListener->Observe(nullptr, "alertclickcallback", mCookie.get());
+  }
+  mBackend->RemoveHandler(mName, this);
+  return S_OK;
+}
+
+HRESULT
+ToastNotificationHandler::OnDismiss(IToastNotification *notification,
+                                    IToastDismissedEventArgs* aArgs)
+{
+  if (mAlertListener) {
+    mAlertListener->Observe(nullptr, "alertfinished", mCookie.get());
+  }
+  mBackend->RemoveHandler(mName, this);
+  return S_OK;
+}
+
+HRESULT
+ToastNotificationHandler::OnFail(IToastNotification *notification,
+                                 IToastFailedEventArgs* aArgs)
+{
+  if (mAlertListener) {
+    mAlertListener->Observe(nullptr, "alertfinished", mCookie.get());
+  }
+  mBackend->RemoveHandler(mName, this);
+  return S_OK;
+}
+
+nsresult
+ToastNotificationHandler::TryShowAlert()
+{
+  if (NS_WARN_IF(!ShowAlert())) {
+    mBackend->RemoveHandler(mName, this);
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+NS_IMETHODIMP
+ToastNotificationHandler::OnImageMissing(nsISupports*)
+{
+  return TryShowAlert();
+}
+
+NS_IMETHODIMP
+ToastNotificationHandler::OnImageReady(nsISupports*, imgIRequest* aRequest)
+{
+  nsresult rv = AsyncSaveImage(aRequest);
+  if (NS_FAILED(rv)) {
+    return TryShowAlert();
+  }
+  return rv;
+}
+
+nsresult
+ToastNotificationHandler::AsyncSaveImage(imgIRequest* aRequest)
+{
+  nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
+                                       getter_AddRefs(mImageFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mImageFile->Append(NS_LITERAL_STRING("notificationimages"));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mImageFile->Create(nsIFile::DIRECTORY_TYPE, 0500);
+  if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIUUIDGenerator> idGen =
+    do_GetService("@mozilla.org/uuid-generator;1", &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsID uuid;
+  rv = idGen->GenerateUUIDInPlace(&uuid);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  char uuidChars[NSID_LENGTH];
+  uuid.ToProvidedString(uuidChars);
+  // Remove the brackets at the beginning and ending of the generated UUID.
+  nsAutoCString uuidStr(Substring(uuidChars + 1, uuidChars + NSID_LENGTH - 2));
+  uuidStr.AppendLiteral(".bmp");
+  mImageFile->AppendNative(uuidStr);
+
+  nsCOMPtr<imgIContainer> imgContainer;
+  rv = aRequest->GetImage(getter_AddRefs(imgContainer));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsMainThreadPtrHandle<ToastNotificationHandler> self(
+    new nsMainThreadPtrHolder<ToastNotificationHandler>(
+      "ToastNotificationHandler", this));
+
+  nsCOMPtr<nsIFile> imageFile(mImageFile);
+  RefPtr<mozilla::gfx::SourceSurface> surface =
+    imgContainer->GetFrame(imgIContainer::FRAME_FIRST,
+                           imgIContainer::FLAG_SYNC_DECODE);
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+    "ToastNotificationHandler::AsyncWriteBitmap",
+    [self, imageFile, surface]() -> void {
+
+      nsresult rv;
+      if (!surface) {
+        rv = NS_ERROR_FAILURE;
+      } else {
+        rv = WinUtils::WriteBitmap(imageFile, surface);
+      }
+
+      nsCOMPtr<nsIRunnable> cbRunnable = NS_NewRunnableFunction(
+        "ToastNotificationHandler::AsyncWriteBitmapCb",
+        [self, rv]() -> void {
+          auto handler = const_cast<ToastNotificationHandler*>(self.get());
+          handler->OnWriteBitmapFinished(rv);
+        });
+
+      NS_DispatchToMainThread(cbRunnable);
+    });
+
+  return mBackend->BackgroundDispatch(r);
+}
+
+void
+ToastNotificationHandler::OnWriteBitmapFinished(nsresult rv)
+{
+  if (NS_SUCCEEDED(rv)) {
+    OnWriteBitmapSuccess();
+  }
+  TryShowAlert();
+}
+
+nsresult
+ToastNotificationHandler::OnWriteBitmapSuccess()
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIURI> fileURI;
+  rv = NS_NewFileURI(getter_AddRefs(fileURI), mImageFile);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoCString uriStr;
+  rv = fileURI->GetSpec(uriStr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  AppendUTF8toUTF16(uriStr, mImageUri);
+
+  mHasImage = true;
+
+  return NS_OK;
+}
+
+} // namespace widget
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/windows/ToastNotificationHandler.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef ToastNotificationHandler_h__
+#define ToastNotificationHandler_h__
+
+#include <windows.ui.notifications.h>
+#include <windows.data.xml.dom.h>
+#include <wrl.h>
+#include "imgIContainer.h"
+#include "nsCOMPtr.h"
+#include "nsIAlertsService.h"
+#include "nsICancelable.h"
+#include "nsIFile.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace widget {
+
+class ToastNotification;
+
+class ToastNotificationHandler final : public nsIAlertNotificationImageListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIALERTNOTIFICATIONIMAGELISTENER
+
+  ToastNotificationHandler(ToastNotification* backend, nsIObserver* aAlertListener,
+                           const nsAString& aName, const nsAString& aCookie,
+                           const nsAString& aTitle, const nsAString& aMsg,
+                           bool aClickable)
+    : mBackend(backend)
+    , mHasImage(false)
+    , mAlertListener(aAlertListener)
+    , mName(aName)
+    , mCookie(aCookie)
+    , mTitle(aTitle)
+    , mMsg(aMsg)
+    , mClickable(aClickable)
+  {
+  }
+
+  nsresult InitAlertAsync(nsIAlertNotification* aAlert);
+
+  void OnWriteBitmapFinished(nsresult rv);
+
+protected:
+  virtual ~ToastNotificationHandler();
+
+  typedef ABI::Windows::Data::Xml::Dom::IXmlDocument IXmlDocument;
+  typedef ABI::Windows::UI::Notifications::IToastNotifier
+          IToastNotifier;
+  typedef ABI::Windows::UI::Notifications::IToastNotification
+          IToastNotification;
+  typedef ABI::Windows::UI::Notifications::IToastDismissedEventArgs
+          IToastDismissedEventArgs;
+  typedef ABI::Windows::UI::Notifications::IToastFailedEventArgs
+          IToastFailedEventArgs;
+  typedef ABI::Windows::UI::Notifications::ToastTemplateType ToastTemplateType;
+
+  Microsoft::WRL::ComPtr<IToastNotification> mNotification;
+  Microsoft::WRL::ComPtr<IToastNotifier> mNotifier;
+
+  RefPtr<ToastNotification> mBackend;
+
+  nsCOMPtr<nsICancelable> mImageRequest;
+  nsCOMPtr<nsIFile> mImageFile;
+  nsString mImageUri;
+  bool mHasImage;
+
+  EventRegistrationToken mActivatedToken;
+  EventRegistrationToken mDismissedToken;
+  EventRegistrationToken mFailedToken;
+
+  nsCOMPtr<nsIObserver> mAlertListener;
+  nsString mName;
+  nsString mCookie;
+  nsString mTitle;
+  nsString mMsg;
+  bool mClickable;
+
+  nsresult TryShowAlert();
+  bool ShowAlert();
+  nsresult AsyncSaveImage(imgIRequest* aRequest);
+  nsresult OnWriteBitmapSuccess();
+
+  bool CreateWindowsNotificationFromXml(IXmlDocument* aToastXml);
+  Microsoft::WRL::ComPtr<IXmlDocument> InitializeXmlForTemplate(
+                         ToastTemplateType templateType);
+
+  HRESULT OnActivate(IToastNotification *notification,
+                     IInspectable *inspectable);
+  HRESULT OnDismiss(IToastNotification *notification,
+                    IToastDismissedEventArgs* aArgs);
+  HRESULT OnFail(IToastNotification *notification,
+                 IToastFailedEventArgs* aArgs);
+};
+
+} // widget
+} // mozilla
+
+#endif
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -1771,16 +1771,113 @@ WinUtils::SetupKeyModifiersSequence(nsTA
 
 /* static */
 bool
 WinUtils::ShouldHideScrollbars()
 {
   return false;
 }
 
+/* static */
+nsresult
+WinUtils::WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
+{
+  RefPtr<SourceSurface> surface =
+    aImage->GetFrame(imgIContainer::FRAME_FIRST,
+                     imgIContainer::FLAG_SYNC_DECODE);
+  NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
+
+  return WriteBitmap(aFile, surface);
+}
+
+/* static */
+nsresult
+WinUtils::WriteBitmap(nsIFile* aFile, SourceSurface* surface)
+{
+  nsresult rv;
+
+  // For either of the following formats we want to set the biBitCount member
+  // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
+  // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
+  // for the BI_RGB value we use for the biCompression member.
+  MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
+             surface->GetFormat() == SurfaceFormat::B8G8R8X8);
+
+  RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
+  NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
+
+  int32_t width = dataSurface->GetSize().width;
+  int32_t height = dataSurface->GetSize().height;
+  int32_t bytesPerPixel = 4 * sizeof(uint8_t);
+  uint32_t bytesPerRow = bytesPerPixel * width;
+
+  // initialize these bitmap structs which we will later
+  // serialize directly to the head of the bitmap file
+  BITMAPINFOHEADER bmi;
+  bmi.biSize = sizeof(BITMAPINFOHEADER);
+  bmi.biWidth = width;
+  bmi.biHeight = height;
+  bmi.biPlanes = 1;
+  bmi.biBitCount = (WORD)bytesPerPixel*8;
+  bmi.biCompression = BI_RGB;
+  bmi.biSizeImage = bytesPerRow * height;
+  bmi.biXPelsPerMeter = 0;
+  bmi.biYPelsPerMeter = 0;
+  bmi.biClrUsed = 0;
+  bmi.biClrImportant = 0;
+
+  BITMAPFILEHEADER bf;
+  bf.bfType = 0x4D42; // 'BM'
+  bf.bfReserved1 = 0;
+  bf.bfReserved2 = 0;
+  bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
+  bf.bfSize = bf.bfOffBits + bmi.biSizeImage;
+
+  // get a file output stream
+  nsCOMPtr<nsIOutputStream> stream;
+  rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  DataSourceSurface::MappedSurface map;
+  if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // write the bitmap headers and rgb pixel data to the file
+  rv = NS_ERROR_FAILURE;
+  if (stream) {
+    uint32_t written;
+    stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
+    if (written == sizeof(BITMAPFILEHEADER)) {
+      stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written);
+      if (written == sizeof(BITMAPINFOHEADER)) {
+        // write out the image data backwards because the desktop won't
+        // show bitmaps with negative heights for top-to-bottom
+        uint32_t i = map.mStride * height;
+        do {
+          i -= map.mStride;
+          stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
+          if (written == bytesPerRow) {
+            rv = NS_OK;
+          } else {
+            rv = NS_ERROR_FAILURE;
+            break;
+          }
+        } while (i != 0);
+      }
+    }
+
+    stream->Close();
+  }
+
+  dataSurface->Unmap();
+
+  return rv;
+}
+
 // This is in use here and in dom/events/TouchEvent.cpp
 /* static */
 uint32_t
 WinUtils::IsTouchDeviceSupportPresent()
 {
   int32_t touchCapabilities = ::GetSystemMetrics(SM_DIGITIZER);
   return (touchCapabilities & NID_READY) &&
          (touchCapabilities & (NID_EXTERNAL_TOUCH | NID_INTEGRATED_TOUCH));
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -480,16 +480,21 @@ public:
    */
   static bool ResolveJunctionPointsAndSymLinks(std::wstring& aPath);
   static bool ResolveJunctionPointsAndSymLinks(nsIFile* aPath);
 
   static void Initialize();
 
   static bool ShouldHideScrollbars();
 
+  static nsresult WriteBitmap(nsIFile* aFile, mozilla::gfx::SourceSurface* surface);
+  // This function is a helper, but it cannot be called from the main thread.
+  // Use the one above!
+  static nsresult WriteBitmap(nsIFile* aFile, imgIContainer* aImage);
+
   /**
    * This function normalizes the input path, converts short filenames to long
    * filenames, and substitutes environment variables for system paths.
    * The resulting output string length is guaranteed to be <= MAX_PATH.
    */
   static bool SanitizePath(const wchar_t* aInputPath, nsAString& aOutput);
 
   /**
--- a/widget/windows/moz.build
+++ b/widget/windows/moz.build
@@ -83,16 +83,18 @@ UNIFIED_SOURCES += [
 ]
 
 # The following files cannot be built in unified mode because of name clashes.
 SOURCES += [
     'JumpListBuilder.cpp',
     'nsBidiKeyboard.cpp',
     'nsFilePicker.cpp',
     'nsWidgetFactory.cpp',
+    'ToastNotification.cpp',
+    'ToastNotificationHandler.cpp',
     'WinCompositorWidget.cpp',
     'WindowsUIUtils.cpp',
     'WinMouseScrollHandler.cpp',
 ]
 
 if CONFIG['NS_PRINTING']:
     UNIFIED_SOURCES += [
         'nsDeviceContextSpecWin.cpp',
@@ -154,9 +156,11 @@ RESFILE = 'widget.res'
 CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
 
 OS_LIBS += [
     'rpcrt4',
 ]
 
 if CONFIG['CC_TYPE'] in ('msvc', 'clang-cl'):
     # C5038: Suppress initializer list order warnings from wrl.h
+    SOURCES['ToastNotification.cpp'].flags += ['-wd5038']
+    SOURCES['ToastNotificationHandler.cpp'].flags += ['-wd5038']
     SOURCES['WindowsUIUtils.cpp'].flags += ['-wd5038']
--- a/widget/windows/nsWidgetFactory.cpp
+++ b/widget/windows/nsWidgetFactory.cpp
@@ -39,16 +39,19 @@
 #include "nsBidiKeyboard.h"
 #include "nsDragService.h"
 #include "nsTransferable.h"
 #include "nsHTMLFormatConverter.h"
 
 #include "WinTaskbar.h"
 #include "JumpListBuilder.h"
 #include "JumpListItem.h"
+// Toast notification support
+#include "ToastNotification.h"
+#include "nsToolkitCompsCID.h"
 
 #include "WindowsUIUtils.h"
 
 #ifdef NS_PRINTING
 #include "nsDeviceContextSpecWin.h"
 #include "nsPrintDialogWin.h"
 #include "nsPrintSettingsServiceWin.h"
 #include "nsPrintSession.h"
@@ -132,16 +135,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListI
 NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListSeparator)
 NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListLink)
 NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListShortcut)
 NS_GENERIC_FACTORY_CONSTRUCTOR(WindowsUIUtils)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(ToastNotification, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(TaskbarPreviewCallback)
 #ifdef NS_PRINTING
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintDialogServiceWin, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSettingsServiceWin, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsPrinterEnumeratorWin)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecWin)
 #endif
@@ -171,16 +175,17 @@ NS_DEFINE_NAMED_CID(NS_WIN_TASKBAR_CID);
 NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTBUILDER_CID);
 NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTITEM_CID);
 NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTSEPARATOR_CID);
 NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTLINK_CID);
 NS_DEFINE_NAMED_CID(NS_WIN_JUMPLISTSHORTCUT_CID);
 NS_DEFINE_NAMED_CID(NS_WINDOWS_UIUTILS_CID);
 NS_DEFINE_NAMED_CID(NS_DRAGSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_BIDIKEYBOARD_CID);
+NS_DEFINE_NAMED_CID(NS_SYSTEMALERTSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_TASKBARPREVIEWCALLBACK_CID);
 #ifdef NS_PRINTING
 NS_DEFINE_NAMED_CID(NS_PRINTDIALOGSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_PRINTSETTINGSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_PRINTER_ENUMERATOR_CID);
 NS_DEFINE_NAMED_CID(NS_PRINTSESSION_CID);
 NS_DEFINE_NAMED_CID(NS_DEVICE_CONTEXT_SPEC_CID);
 #endif
@@ -206,16 +211,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_WIN_JUMPLISTBUILDER_CID, false, nullptr, JumpListBuilderConstructor },
   { &kNS_WIN_JUMPLISTITEM_CID, false, nullptr, JumpListItemConstructor },
   { &kNS_WIN_JUMPLISTSEPARATOR_CID, false, nullptr, JumpListSeparatorConstructor },
   { &kNS_WIN_JUMPLISTLINK_CID, false, nullptr, JumpListLinkConstructor },
   { &kNS_WIN_JUMPLISTSHORTCUT_CID, false, nullptr, JumpListShortcutConstructor },
   { &kNS_WINDOWS_UIUTILS_CID, false, nullptr, WindowsUIUtilsConstructor },
   { &kNS_DRAGSERVICE_CID, false, nullptr, nsDragServiceConstructor, Module::MAIN_PROCESS_ONLY },
   { &kNS_BIDIKEYBOARD_CID, false, nullptr, nsBidiKeyboardConstructor, Module::MAIN_PROCESS_ONLY },
+  { &kNS_SYSTEMALERTSSERVICE_CID, false, nullptr, ToastNotificationConstructor },
   { &kNS_TASKBARPREVIEWCALLBACK_CID, false, nullptr, TaskbarPreviewCallbackConstructor },
 #ifdef NS_PRINTING
   { &kNS_PRINTDIALOGSERVICE_CID, false, nullptr, nsPrintDialogServiceWinConstructor, Module::MAIN_PROCESS_ONLY },
   { &kNS_PRINTSETTINGSSERVICE_CID, false, nullptr, nsPrintSettingsServiceWinConstructor },
   { &kNS_PRINTER_ENUMERATOR_CID, false, nullptr, nsPrinterEnumeratorWinConstructor },
   { &kNS_PRINTSESSION_CID, false, nullptr, nsPrintSessionConstructor },
   { &kNS_DEVICE_CONTEXT_SPEC_CID, false, nullptr, nsDeviceContextSpecWinConstructor },
 #endif
@@ -241,16 +247,17 @@ static const mozilla::Module::ContractID
   { "@mozilla.org/windows-jumplistbuilder;1", &kNS_WIN_JUMPLISTBUILDER_CID },
   { "@mozilla.org/windows-jumplistitem;1", &kNS_WIN_JUMPLISTITEM_CID },
   { "@mozilla.org/windows-jumplistseparator;1", &kNS_WIN_JUMPLISTSEPARATOR_CID },
   { "@mozilla.org/windows-jumplistlink;1", &kNS_WIN_JUMPLISTLINK_CID },
   { "@mozilla.org/windows-jumplistshortcut;1", &kNS_WIN_JUMPLISTSHORTCUT_CID },
   { "@mozilla.org/windows-ui-utils;1", &kNS_WINDOWS_UIUTILS_CID },
   { "@mozilla.org/widget/dragservice;1", &kNS_DRAGSERVICE_CID, Module::MAIN_PROCESS_ONLY },
   { "@mozilla.org/widget/bidikeyboard;1", &kNS_BIDIKEYBOARD_CID, Module::MAIN_PROCESS_ONLY },
+  { NS_SYSTEMALERTSERVICE_CONTRACTID, &kNS_SYSTEMALERTSSERVICE_CID, Module::MAIN_PROCESS_ONLY },
   { "@mozilla.org/widget/taskbar-preview-callback;1", &kNS_TASKBARPREVIEWCALLBACK_CID },
 #ifdef NS_PRINTING
   { NS_PRINTDIALOGSERVICE_CONTRACTID, &kNS_PRINTDIALOGSERVICE_CID },
   { "@mozilla.org/gfx/printsettings-service;1", &kNS_PRINTSETTINGSSERVICE_CID },
   { "@mozilla.org/gfx/printerenumerator;1", &kNS_PRINTER_ENUMERATOR_CID },
   { "@mozilla.org/gfx/printsession;1", &kNS_PRINTSESSION_CID },
   { "@mozilla.org/gfx/devicecontextspec;1", &kNS_DEVICE_CONTEXT_SPEC_CID },
 #endif