diff options
Diffstat (limited to '')
-rw-r--r-- | widget/windows/ToastNotification.cpp | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/widget/windows/ToastNotification.cpp b/widget/windows/ToastNotification.cpp new file mode 100644 index 0000000000..916f770590 --- /dev/null +++ b/widget/windows/ToastNotification.cpp @@ -0,0 +1,204 @@ +/* -*- 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" +#include "WinTaskbar.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; + } + + nsAutoString uid; + if (NS_WARN_IF(!WinTaskbar::GetAppUserModelID(uid))) { + // Windows Toast Notification requires AppId + 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). + for (auto iter = mActiveHandlers.Iter(); !iter.Done(); iter.Next()) { + RefPtr<ToastNotificationHandler> handler = iter.UserData(); + iter.Remove(); + + // Break the cycle between the handler and the MSCOM notification so the + // handler's destructor will be called. + handler->UnregisterHandler(); + } + + 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; + } + + nsAutoString hostPort; + rv = aAlert->GetSource(hostPort); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + RefPtr<ToastNotificationHandler> oldHandler = mActiveHandlers.Get(name); + + RefPtr<ToastNotificationHandler> handler = new ToastNotificationHandler( + this, aAlertListener, name, cookie, title, text, hostPort, textClickable); + mActiveHandlers.Put(name, RefPtr{handler}); + + rv = handler->InitAlertAsync(aAlert); + if (NS_WARN_IF(NS_FAILED(rv))) { + mActiveHandlers.Remove(name); + handler->UnregisterHandler(); + return rv; + } + + // If there was a previous handler with the same name then unregister it. + if (oldHandler) { + oldHandler->UnregisterHandler(); + } + + return NS_OK; +} + +NS_IMETHODIMP +ToastNotification::CloseAlert(const nsAString& aAlertName) { + RefPtr<ToastNotificationHandler> handler; + if (NS_WARN_IF(!mActiveHandlers.Get(aAlertName, getter_AddRefs(handler)))) { + return NS_OK; + } + mActiveHandlers.Remove(aAlertName); + handler->UnregisterHandler(); + 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); + aHandler->UnregisterHandler(); + } +} + +} // namespace widget +} // namespace mozilla |