Backed out changeset b06f19b95b94 (bug 1784438) for causing build bustages CLOSED...
[gecko.git] / toolkit / components / alerts / nsAlertsService.cpp
blob66c8968b7ba90b8d4431b83deae0e5ba39f638ec
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/. */
6 #include "xpcpublic.h"
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"
14 #include "nsXPCOM.h"
15 #include "nsPromiseFlatString.h"
16 #include "nsToolkitCompsCID.h"
17 #include "nsComponentManagerUtils.h"
19 #ifdef MOZ_PLACES
20 # include "nsIFaviconService.h"
21 #endif // MOZ_PLACES
23 #ifdef XP_WIN
24 # include <shellapi.h>
25 #endif
27 using namespace mozilla;
29 namespace {
31 #ifdef MOZ_PLACES
33 class IconCallback final : public nsIFaviconDataCallback {
34 public:
35 NS_DECL_ISUPPORTS
37 IconCallback(nsIAlertsService* aBackend, nsIAlertNotification* aAlert,
38 nsIObserver* aAlertListener)
39 : mBackend(aBackend), mAlert(aAlert), mAlertListener(aAlertListener) {}
41 NS_IMETHOD
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;
45 if (aIconSize > 0) {
46 nsCOMPtr<nsIAlertsIconData> alertsIconData(do_QueryInterface(mBackend));
47 if (alertsIconData) {
48 rv = alertsIconData->ShowAlertWithIconData(mAlert, mAlertListener,
49 aIconSize, aIconData);
51 } else if (aIconURI) {
52 nsCOMPtr<nsIAlertsIconURI> alertsIconURI(do_QueryInterface(mBackend));
53 if (alertsIconURI) {
54 rv = alertsIconURI->ShowAlertWithIconURI(mAlert, mAlertListener,
55 aIconURI);
58 if (NS_FAILED(rv)) {
59 rv = mBackend->ShowAlert(mAlert, mAlertListener);
61 return rv;
64 private:
65 virtual ~IconCallback() = default;
67 nsCOMPtr<nsIAlertsService> mBackend;
68 nsCOMPtr<nsIAlertNotification> mAlert;
69 nsCOMPtr<nsIObserver> mAlertListener;
72 NS_IMPL_ISUPPORTS(IconCallback, nsIFaviconDataCallback)
74 #endif // MOZ_PLACES
76 nsresult ShowWithIconBackend(nsIAlertsService* aBackend,
77 nsIAlertNotification* aAlert,
78 nsIObserver* aAlertListener) {
79 #ifdef MOZ_PLACES
80 nsCOMPtr<nsIURI> uri;
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);
106 #else
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)) {
117 return 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() {
137 bool result = true;
139 #ifdef XP_WIN
140 if (!xpc::IsInAutomation()) {
141 QUERY_USER_NOTIFICATION_STATE qstate;
142 if (SUCCEEDED(SHQueryUserNotificationState(&qstate))) {
143 if (qstate != QUNS_ACCEPTS_NOTIFICATIONS) {
144 result = false;
148 #endif
150 nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(GetDNDBackend());
151 if (alertsDND) {
152 bool suppressForScreenSharing = false;
153 nsresult rv =
154 alertsDND->GetSuppressForScreenSharing(&suppressForScreenSharing);
155 if (NS_SUCCEEDED(rv)) {
156 result &= !suppressForScreenSharing;
160 return result;
163 bool nsAlertsService::ShouldUseSystemBackend() {
164 if (!mBackend) {
165 return false;
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);
180 // vibrate is unused
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.
194 return false;
195 #else
196 // The system may not have the notification library, we should fall back to
197 // XUL.
198 return true;
199 #endif
202 NS_IMETHODIMP nsAlertsService::ShowAlert(nsIAlertNotification* aAlert,
203 nsIObserver* aAlertListener) {
204 NS_ENSURE_ARG(aAlert);
206 nsAutoString cookie;
207 nsresult rv = aAlert->GetCookie(cookie);
208 NS_ENSURE_SUCCESS(rv, rv);
210 // Check if there is an optional service that handles system-level
211 // notifications
212 if (ShouldUseSystemBackend()) {
213 rv = ShowWithBackend(mBackend, aAlert, aAlertListener);
214 if (NS_SUCCEEDED(rv) || !ShouldFallBackToXUL()) {
215 return rv;
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.
219 mBackend = nullptr;
222 if (!ShouldShowAlert()) {
223 // Do not display the alert. Instead call alertfinished and get out.
224 if (aAlertListener)
225 aAlertListener->Observe(nullptr, "alertfinished", cookie.get());
226 return NS_OK;
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) {
237 nsresult rv;
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
243 // future alerts.
244 mBackend = nullptr;
246 } else {
247 nsCOMPtr<nsIAlertsService> xulBackend(nsXULAlerts::GetInstance());
248 NS_ENSURE_TRUE(xulBackend, NS_ERROR_FAILURE);
249 rv = xulBackend->CloseAlert(aAlertName, aContextClosed);
251 return rv;
254 // nsIAlertsDoNotDisturb
255 NS_IMETHODIMP nsAlertsService::GetManualDoNotDisturb(bool* aRetVal) {
256 #ifdef MOZ_WIDGET_ANDROID
257 return NS_ERROR_NOT_IMPLEMENTED;
258 #else
259 nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(GetDNDBackend());
260 NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED);
261 return alertsDND->GetManualDoNotDisturb(aRetVal);
262 #endif
265 NS_IMETHODIMP nsAlertsService::SetManualDoNotDisturb(bool aDoNotDisturb) {
266 #ifdef MOZ_WIDGET_ANDROID
267 return NS_ERROR_NOT_IMPLEMENTED;
268 #else
269 nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(GetDNDBackend());
270 NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED);
272 return alertsDND->SetManualDoNotDisturb(aDoNotDisturb);
273 #endif
276 NS_IMETHODIMP nsAlertsService::GetSuppressForScreenSharing(bool* aRetVal) {
277 #ifdef MOZ_WIDGET_ANDROID
278 return NS_ERROR_NOT_IMPLEMENTED;
279 #else
280 nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(GetDNDBackend());
281 NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED);
282 return alertsDND->GetSuppressForScreenSharing(aRetVal);
283 #endif
286 NS_IMETHODIMP nsAlertsService::SetSuppressForScreenSharing(bool aSuppress) {
287 #ifdef MOZ_WIDGET_ANDROID
288 return NS_ERROR_NOT_IMPLEMENTED;
289 #else
290 nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(GetDNDBackend());
291 NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED);
292 return alertsDND->SetSuppressForScreenSharing(aSuppress);
293 #endif
296 already_AddRefed<nsIAlertsDoNotDisturb> nsAlertsService::GetDNDBackend() {
297 nsCOMPtr<nsIAlertsService> backend;
298 // Try the system notification service.
299 if (ShouldUseSystemBackend()) {
300 backend = mBackend;
302 if (!backend) {
303 backend = nsXULAlerts::GetInstance();
306 nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(do_QueryInterface(backend));
307 return alertsDND.forget();