1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/PermissionMessageUtils.h"
8 #include "mozilla/Preferences.h"
9 #include "mozilla/StaticPrefs_alerts.h"
10 #include "nsXULAppAPI.h"
12 #include "nsAlertsService.h"
15 #include "nsPromiseFlatString.h"
16 #include "nsToolkitCompsCID.h"
17 #include "nsComponentManagerUtils.h"
20 # include "nsIFaviconService.h"
24 # include <shellapi.h>
27 using namespace mozilla
;
33 class IconCallback final
: public nsIFaviconDataCallback
{
37 IconCallback(nsIAlertsService
* aBackend
, nsIAlertNotification
* aAlert
,
38 nsIObserver
* aAlertListener
)
39 : mBackend(aBackend
), mAlert(aAlert
), mAlertListener(aAlertListener
) {}
42 OnComplete(nsIURI
* aIconURI
, uint32_t aIconSize
, const uint8_t* aIconData
,
43 const nsACString
& aMimeType
, uint16_t aWidth
) override
{
44 nsresult rv
= NS_ERROR_FAILURE
;
46 nsCOMPtr
<nsIAlertsIconData
> alertsIconData(do_QueryInterface(mBackend
));
48 rv
= alertsIconData
->ShowAlertWithIconData(mAlert
, mAlertListener
,
49 aIconSize
, aIconData
);
51 } else if (aIconURI
) {
52 nsCOMPtr
<nsIAlertsIconURI
> alertsIconURI(do_QueryInterface(mBackend
));
54 rv
= alertsIconURI
->ShowAlertWithIconURI(mAlert
, mAlertListener
,
59 rv
= mBackend
->ShowAlert(mAlert
, mAlertListener
);
65 virtual ~IconCallback() = default;
67 nsCOMPtr
<nsIAlertsService
> mBackend
;
68 nsCOMPtr
<nsIAlertNotification
> mAlert
;
69 nsCOMPtr
<nsIObserver
> mAlertListener
;
72 NS_IMPL_ISUPPORTS(IconCallback
, nsIFaviconDataCallback
)
76 nsresult
ShowWithIconBackend(nsIAlertsService
* aBackend
,
77 nsIAlertNotification
* aAlert
,
78 nsIObserver
* aAlertListener
) {
81 nsresult rv
= aAlert
->GetURI(getter_AddRefs(uri
));
82 if (NS_FAILED(rv
) || !uri
) {
83 return NS_ERROR_FAILURE
;
86 // Ensure the backend supports favicons.
87 nsCOMPtr
<nsIAlertsIconData
> alertsIconData(do_QueryInterface(aBackend
));
88 nsCOMPtr
<nsIAlertsIconURI
> alertsIconURI
;
89 if (!alertsIconData
) {
90 alertsIconURI
= do_QueryInterface(aBackend
);
92 if (!alertsIconData
&& !alertsIconURI
) {
93 return NS_ERROR_NOT_IMPLEMENTED
;
96 nsCOMPtr
<nsIFaviconService
> favicons(
97 do_GetService("@mozilla.org/browser/favicon-service;1"));
98 NS_ENSURE_TRUE(favicons
, NS_ERROR_FAILURE
);
100 nsCOMPtr
<nsIFaviconDataCallback
> callback
=
101 new IconCallback(aBackend
, aAlert
, aAlertListener
);
102 if (alertsIconData
) {
103 return favicons
->GetFaviconDataForPage(uri
, callback
, 0);
105 return favicons
->GetFaviconURLForPage(uri
, callback
, 0);
107 return NS_ERROR_NOT_IMPLEMENTED
;
108 #endif // !MOZ_PLACES
111 nsresult
ShowWithBackend(nsIAlertsService
* aBackend
,
112 nsIAlertNotification
* aAlert
,
113 nsIObserver
* aAlertListener
) {
114 if (Preferences::GetBool("alerts.showFavicons")) {
115 nsresult rv
= ShowWithIconBackend(aBackend
, aAlert
, aAlertListener
);
116 if (NS_SUCCEEDED(rv
)) {
121 // If favicons are disabled, or the backend doesn't support them, show the
122 // alert without one.
123 return aBackend
->ShowAlert(aAlert
, aAlertListener
);
126 } // anonymous namespace
128 NS_IMPL_ISUPPORTS(nsAlertsService
, nsIAlertsService
, nsIAlertsDoNotDisturb
)
130 nsAlertsService::nsAlertsService() : mBackend(nullptr) {
131 mBackend
= do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID
);
134 nsAlertsService::~nsAlertsService() = default;
136 bool nsAlertsService::ShouldShowAlert() {
140 if (!xpc::IsInAutomation()) {
141 QUERY_USER_NOTIFICATION_STATE qstate
;
142 if (SUCCEEDED(SHQueryUserNotificationState(&qstate
))) {
143 if (qstate
!= QUNS_ACCEPTS_NOTIFICATIONS
) {
150 nsCOMPtr
<nsIAlertsDoNotDisturb
> alertsDND(GetDNDBackend());
152 bool suppressForScreenSharing
= false;
154 alertsDND
->GetSuppressForScreenSharing(&suppressForScreenSharing
);
155 if (NS_SUCCEEDED(rv
)) {
156 result
&= !suppressForScreenSharing
;
163 bool nsAlertsService::ShouldUseSystemBackend() {
167 return StaticPrefs::alerts_useSystemBackend();
170 NS_IMETHODIMP
nsAlertsService::ShowAlertNotification(
171 const nsAString
& aImageUrl
, const nsAString
& aAlertTitle
,
172 const nsAString
& aAlertText
, bool aAlertTextClickable
,
173 const nsAString
& aAlertCookie
, nsIObserver
* aAlertListener
,
174 const nsAString
& aAlertName
, const nsAString
& aBidi
, const nsAString
& aLang
,
175 const nsAString
& aData
, nsIPrincipal
* aPrincipal
, bool aInPrivateBrowsing
,
176 bool aRequireInteraction
) {
177 nsCOMPtr
<nsIAlertNotification
> alert
=
178 do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID
);
179 NS_ENSURE_TRUE(alert
, NS_ERROR_FAILURE
);
181 nsTArray
<uint32_t> vibrate
;
182 nsresult rv
= alert
->Init(aAlertName
, aImageUrl
, aAlertTitle
, aAlertText
,
183 aAlertTextClickable
, aAlertCookie
, aBidi
, aLang
,
184 aData
, aPrincipal
, aInPrivateBrowsing
,
185 aRequireInteraction
, false, vibrate
);
186 NS_ENSURE_SUCCESS(rv
, rv
);
187 return ShowAlert(alert
, aAlertListener
);
190 static bool ShouldFallBackToXUL() {
191 #if defined(XP_WIN) || defined(XP_MACOSX)
192 // We know we always have system backend on Windows and macOS. Let's not
193 // permanently fall back to XUL just because of temporary failure.
196 // The system may not have the notification library, we should fall back to
202 NS_IMETHODIMP
nsAlertsService::ShowAlert(nsIAlertNotification
* aAlert
,
203 nsIObserver
* aAlertListener
) {
204 NS_ENSURE_ARG(aAlert
);
207 nsresult rv
= aAlert
->GetCookie(cookie
);
208 NS_ENSURE_SUCCESS(rv
, rv
);
210 // Check if there is an optional service that handles system-level
212 if (ShouldUseSystemBackend()) {
213 rv
= ShowWithBackend(mBackend
, aAlert
, aAlertListener
);
214 if (NS_SUCCEEDED(rv
) || !ShouldFallBackToXUL()) {
217 // If the system backend failed to show the alert, clear the backend and
218 // retry with XUL notifications. Future alerts will always use XUL.
222 if (!ShouldShowAlert()) {
223 // Do not display the alert. Instead call alertfinished and get out.
225 aAlertListener
->Observe(nullptr, "alertfinished", cookie
.get());
229 // Use XUL notifications as a fallback if above methods have failed.
230 nsCOMPtr
<nsIAlertsService
> xulBackend(nsXULAlerts::GetInstance());
231 NS_ENSURE_TRUE(xulBackend
, NS_ERROR_FAILURE
);
232 return ShowWithBackend(xulBackend
, aAlert
, aAlertListener
);
235 NS_IMETHODIMP
nsAlertsService::CloseAlert(const nsAString
& aAlertName
,
236 bool aContextClosed
) {
238 // Try the system notification service.
239 if (ShouldUseSystemBackend()) {
240 rv
= mBackend
->CloseAlert(aAlertName
, aContextClosed
);
241 if (NS_WARN_IF(NS_FAILED(rv
)) && ShouldFallBackToXUL()) {
242 // If the system backend failed to close the alert, fall back to XUL for
247 nsCOMPtr
<nsIAlertsService
> xulBackend(nsXULAlerts::GetInstance());
248 NS_ENSURE_TRUE(xulBackend
, NS_ERROR_FAILURE
);
249 rv
= xulBackend
->CloseAlert(aAlertName
, aContextClosed
);
254 // nsIAlertsDoNotDisturb
255 NS_IMETHODIMP
nsAlertsService::GetManualDoNotDisturb(bool* aRetVal
) {
256 #ifdef MOZ_WIDGET_ANDROID
257 return NS_ERROR_NOT_IMPLEMENTED
;
259 nsCOMPtr
<nsIAlertsDoNotDisturb
> alertsDND(GetDNDBackend());
260 NS_ENSURE_TRUE(alertsDND
, NS_ERROR_NOT_IMPLEMENTED
);
261 return alertsDND
->GetManualDoNotDisturb(aRetVal
);
265 NS_IMETHODIMP
nsAlertsService::SetManualDoNotDisturb(bool aDoNotDisturb
) {
266 #ifdef MOZ_WIDGET_ANDROID
267 return NS_ERROR_NOT_IMPLEMENTED
;
269 nsCOMPtr
<nsIAlertsDoNotDisturb
> alertsDND(GetDNDBackend());
270 NS_ENSURE_TRUE(alertsDND
, NS_ERROR_NOT_IMPLEMENTED
);
272 return alertsDND
->SetManualDoNotDisturb(aDoNotDisturb
);
276 NS_IMETHODIMP
nsAlertsService::GetSuppressForScreenSharing(bool* aRetVal
) {
277 #ifdef MOZ_WIDGET_ANDROID
278 return NS_ERROR_NOT_IMPLEMENTED
;
280 nsCOMPtr
<nsIAlertsDoNotDisturb
> alertsDND(GetDNDBackend());
281 NS_ENSURE_TRUE(alertsDND
, NS_ERROR_NOT_IMPLEMENTED
);
282 return alertsDND
->GetSuppressForScreenSharing(aRetVal
);
286 NS_IMETHODIMP
nsAlertsService::SetSuppressForScreenSharing(bool aSuppress
) {
287 #ifdef MOZ_WIDGET_ANDROID
288 return NS_ERROR_NOT_IMPLEMENTED
;
290 nsCOMPtr
<nsIAlertsDoNotDisturb
> alertsDND(GetDNDBackend());
291 NS_ENSURE_TRUE(alertsDND
, NS_ERROR_NOT_IMPLEMENTED
);
292 return alertsDND
->SetSuppressForScreenSharing(aSuppress
);
296 already_AddRefed
<nsIAlertsDoNotDisturb
> nsAlertsService::GetDNDBackend() {
297 nsCOMPtr
<nsIAlertsService
> backend
;
298 // Try the system notification service.
299 if (ShouldUseSystemBackend()) {
303 backend
= nsXULAlerts::GetInstance();
306 nsCOMPtr
<nsIAlertsDoNotDisturb
> alertsDND(do_QueryInterface(backend
));
307 return alertsDND
.forget();