/* -*- Mode: C++; tab-width: 2; 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/. */ #include "nsIObserverService.h" #include "xpcpublic.h" #include "mozilla/AppShutdown.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/Services.h" #include "mozilla/StaticPrefs_alerts.h" #include "nsServiceManagerUtils.h" #include "nsXULAlerts.h" #include "nsAlertsService.h" #include "nsToolkitCompsCID.h" #include "nsComponentManagerUtils.h" #ifdef MOZ_PLACES # include "nsIFaviconService.h" #endif // MOZ_PLACES #ifdef XP_WIN # include # include #endif using namespace mozilla; NS_IMPL_ISUPPORTS(nsAlertsService, nsIAlertsService, nsIAlertsDoNotDisturb, nsIObserver) nsAlertsService::nsAlertsService() : mBackend(nullptr) { mBackend = do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID); } nsresult nsAlertsService::Init() { if (nsCOMPtr obsServ = mozilla::services::GetObserverService()) { (void)NS_WARN_IF( NS_FAILED(obsServ->AddObserver(this, "last-pb-context-exited", false))); } // The shutdown callback holds a strong reference and thus makes sure this // runs at shutdown. // // Note that the purpose of this shutdown cleanup is to make the leak checker // happy, and an early exit(0) without calling it should not break anything. // (See also bug 1606879) RunOnShutdown([self = RefPtr{this}]() { self->Teardown(); }); return NS_OK; } nsAlertsService::~nsAlertsService() = default; bool nsAlertsService::ShouldShowAlert() { bool result = true; #ifdef XP_WIN if (!xpc::IsInAutomation()) { QUERY_USER_NOTIFICATION_STATE qstate; if (SUCCEEDED(SHQueryUserNotificationState(&qstate))) { if (qstate != QUNS_ACCEPTS_NOTIFICATIONS) { result = false; } } } #endif nsCOMPtr alertsDND(GetDNDBackend()); if (alertsDND) { bool suppressForScreenSharing = false; nsresult rv = alertsDND->GetSuppressForScreenSharing(&suppressForScreenSharing); if (NS_SUCCEEDED(rv)) { result &= !suppressForScreenSharing; } } return result; } NS_IMETHODIMP nsAlertsService::ShowAlert(nsIAlertNotification* aAlert, nsIObserver* aAlertListener) { NS_ENSURE_ARG(aAlert); nsAutoString cookie; nsresult rv = aAlert->GetCookie(cookie); NS_ENSURE_SUCCESS(rv, rv); if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { // Bailing out without calling alertfinished, because we do not want to // propagate an error to observers during shutdown. return NS_OK; } // Check if there is an optional service that handles system-level // notifications if (StaticPrefs::alerts_useSystemBackend()) { if (!mBackend) { return NS_ERROR_NOT_AVAILABLE; } return mBackend->ShowAlert(aAlert, aAlertListener); } if (!ShouldShowAlert()) { // Do not display the alert. Instead call alertfinished and get out. if (aAlertListener) { aAlertListener->Observe(nullptr, "alertfinished", cookie.get()); } return NS_OK; } // Use XUL notifications as a fallback if above methods have failed. nsCOMPtr xulBackend(nsXULAlerts::GetInstance()); NS_ENSURE_TRUE(xulBackend, NS_ERROR_FAILURE); return xulBackend->ShowAlert(aAlert, aAlertListener); } NS_IMETHODIMP nsAlertsService::CloseAlert(const nsAString& aAlertName, bool aContextClosed) { if (!StaticPrefs::alerts_useSystemBackend()) { nsCOMPtr xulBackend(nsXULAlerts::GetInstance()); NS_ENSURE_TRUE(xulBackend, NS_ERROR_FAILURE); return xulBackend->CloseAlert(aAlertName, aContextClosed); } if (!mBackend) { return NS_ERROR_NOT_AVAILABLE; } return mBackend->CloseAlert(aAlertName, aContextClosed); } NS_IMETHODIMP nsAlertsService::GetHistory(nsTArray& aResult) { if (!mBackend) { return NS_OK; } return mBackend->GetHistory(aResult); } // nsIAlertsDoNotDisturb NS_IMETHODIMP nsAlertsService::GetManualDoNotDisturb(bool* aRetVal) { #ifdef MOZ_WIDGET_ANDROID return NS_ERROR_NOT_IMPLEMENTED; #else nsCOMPtr alertsDND(GetDNDBackend()); NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED); return alertsDND->GetManualDoNotDisturb(aRetVal); #endif } NS_IMETHODIMP nsAlertsService::SetManualDoNotDisturb(bool aDoNotDisturb) { #ifdef MOZ_WIDGET_ANDROID return NS_ERROR_NOT_IMPLEMENTED; #else nsCOMPtr alertsDND(GetDNDBackend()); NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED); return alertsDND->SetManualDoNotDisturb(aDoNotDisturb); #endif } NS_IMETHODIMP nsAlertsService::GetSuppressForScreenSharing(bool* aRetVal) { #ifdef MOZ_WIDGET_ANDROID return NS_ERROR_NOT_IMPLEMENTED; #else nsCOMPtr alertsDND(GetDNDBackend()); NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED); return alertsDND->GetSuppressForScreenSharing(aRetVal); #endif } NS_IMETHODIMP nsAlertsService::SetSuppressForScreenSharing(bool aSuppress) { #ifdef MOZ_WIDGET_ANDROID return NS_ERROR_NOT_IMPLEMENTED; #else nsCOMPtr alertsDND(GetDNDBackend()); NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED); return alertsDND->SetSuppressForScreenSharing(aSuppress); #endif } already_AddRefed nsAlertsService::GetDNDBackend() { nsCOMPtr backend; // Try the system notification service. if (StaticPrefs::alerts_useSystemBackend()) { backend = mBackend; } if (!backend) { backend = nsXULAlerts::GetInstance(); } nsCOMPtr alertsDND(do_QueryInterface(backend)); return alertsDND.forget(); } NS_IMETHODIMP nsAlertsService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { nsDependentCString topic(aTopic); if (topic == "last-pb-context-exited"_ns) { return PbmTeardown(); } return NS_OK; } NS_IMETHODIMP nsAlertsService::Teardown() { nsCOMPtr backend; // Try the system notification service. if (StaticPrefs::alerts_useSystemBackend()) { backend = mBackend; } if (!backend) { // We do not try nsXULAlerts here as it already uses ClearOnShutdown. return NS_OK; } return backend->Teardown(); } NS_IMETHODIMP nsAlertsService::PbmTeardown() { nsCOMPtr backend; // Try the system notification service. if (StaticPrefs::alerts_useSystemBackend()) { backend = mBackend; } if (!backend) { backend = nsXULAlerts::GetInstance(); } return backend->PbmTeardown(); }