/src/mozilla-central/toolkit/components/alerts/nsAlertsService.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
5 | | |
6 | | #include "mozilla/dom/ContentChild.h" |
7 | | #include "mozilla/dom/PermissionMessageUtils.h" |
8 | | #include "mozilla/Preferences.h" |
9 | | #include "mozilla/Telemetry.h" |
10 | | #include "nsXULAppAPI.h" |
11 | | |
12 | | #include "nsAlertsService.h" |
13 | | |
14 | | #include "nsXPCOM.h" |
15 | | #include "nsIServiceManager.h" |
16 | | #include "nsIDOMWindow.h" |
17 | | #include "nsPromiseFlatString.h" |
18 | | #include "nsToolkitCompsCID.h" |
19 | | |
20 | | #ifdef MOZ_PLACES |
21 | | #include "nsIFaviconService.h" |
22 | | #endif // MOZ_PLACES |
23 | | |
24 | | #ifdef XP_WIN |
25 | | #include <shellapi.h> |
26 | | #endif |
27 | | |
28 | | using namespace mozilla; |
29 | | |
30 | | using mozilla::dom::ContentChild; |
31 | | |
32 | | namespace { |
33 | | |
34 | | #ifdef MOZ_PLACES |
35 | | |
36 | | class IconCallback final : public nsIFaviconDataCallback |
37 | | { |
38 | | public: |
39 | | NS_DECL_ISUPPORTS |
40 | | |
41 | | IconCallback(nsIAlertsService* aBackend, |
42 | | nsIAlertNotification* aAlert, |
43 | | nsIObserver* aAlertListener) |
44 | | : mBackend(aBackend) |
45 | | , mAlert(aAlert) |
46 | | , mAlertListener(aAlertListener) |
47 | 0 | {} |
48 | | |
49 | | NS_IMETHOD |
50 | | OnComplete(nsIURI *aIconURI, uint32_t aIconSize, const uint8_t *aIconData, |
51 | | const nsACString &aMimeType, uint16_t aWidth) override |
52 | 0 | { |
53 | 0 | nsresult rv = NS_ERROR_FAILURE; |
54 | 0 | if (aIconSize > 0) { |
55 | 0 | nsCOMPtr<nsIAlertsIconData> alertsIconData(do_QueryInterface(mBackend)); |
56 | 0 | if (alertsIconData) { |
57 | 0 | rv = alertsIconData->ShowAlertWithIconData(mAlert, mAlertListener, |
58 | 0 | aIconSize, aIconData); |
59 | 0 | } |
60 | 0 | } else if (aIconURI) { |
61 | 0 | nsCOMPtr<nsIAlertsIconURI> alertsIconURI(do_QueryInterface(mBackend)); |
62 | 0 | if (alertsIconURI) { |
63 | 0 | rv = alertsIconURI->ShowAlertWithIconURI(mAlert, mAlertListener, |
64 | 0 | aIconURI); |
65 | 0 | } |
66 | 0 | } |
67 | 0 | if (NS_FAILED(rv)) { |
68 | 0 | rv = mBackend->ShowAlert(mAlert, mAlertListener); |
69 | 0 | } |
70 | 0 | return rv; |
71 | 0 | } |
72 | | |
73 | | private: |
74 | 0 | virtual ~IconCallback() {} |
75 | | |
76 | | nsCOMPtr<nsIAlertsService> mBackend; |
77 | | nsCOMPtr<nsIAlertNotification> mAlert; |
78 | | nsCOMPtr<nsIObserver> mAlertListener; |
79 | | }; |
80 | | |
81 | | NS_IMPL_ISUPPORTS(IconCallback, nsIFaviconDataCallback) |
82 | | |
83 | | #endif // MOZ_PLACES |
84 | | |
85 | | nsresult |
86 | | ShowWithIconBackend(nsIAlertsService* aBackend, nsIAlertNotification* aAlert, |
87 | | nsIObserver* aAlertListener) |
88 | 0 | { |
89 | 0 | #ifdef MOZ_PLACES |
90 | 0 | nsCOMPtr<nsIURI> uri; |
91 | 0 | nsresult rv = aAlert->GetURI(getter_AddRefs(uri)); |
92 | 0 | if (NS_FAILED(rv) || !uri) { |
93 | 0 | return NS_ERROR_FAILURE; |
94 | 0 | } |
95 | 0 | |
96 | 0 | // Ensure the backend supports favicons. |
97 | 0 | nsCOMPtr<nsIAlertsIconData> alertsIconData(do_QueryInterface(aBackend)); |
98 | 0 | nsCOMPtr<nsIAlertsIconURI> alertsIconURI; |
99 | 0 | if (!alertsIconData) { |
100 | 0 | alertsIconURI = do_QueryInterface(aBackend); |
101 | 0 | } |
102 | 0 | if (!alertsIconData && !alertsIconURI) { |
103 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
104 | 0 | } |
105 | 0 | |
106 | 0 | nsCOMPtr<nsIFaviconService> favicons(do_GetService( |
107 | 0 | "@mozilla.org/browser/favicon-service;1")); |
108 | 0 | NS_ENSURE_TRUE(favicons, NS_ERROR_FAILURE); |
109 | 0 |
|
110 | 0 | nsCOMPtr<nsIFaviconDataCallback> callback = |
111 | 0 | new IconCallback(aBackend, aAlert, aAlertListener); |
112 | 0 | if (alertsIconData) { |
113 | 0 | return favicons->GetFaviconDataForPage(uri, callback, 0); |
114 | 0 | } |
115 | 0 | return favicons->GetFaviconURLForPage(uri, callback, 0); |
116 | | #else |
117 | | return NS_ERROR_NOT_IMPLEMENTED; |
118 | | #endif // !MOZ_PLACES |
119 | | } |
120 | | |
121 | | nsresult |
122 | | ShowWithBackend(nsIAlertsService* aBackend, nsIAlertNotification* aAlert, |
123 | | nsIObserver* aAlertListener, const nsAString& aPersistentData) |
124 | 0 | { |
125 | 0 | if (!aPersistentData.IsEmpty()) { |
126 | 0 | return aBackend->ShowPersistentNotification( |
127 | 0 | aPersistentData, aAlert, aAlertListener); |
128 | 0 | } |
129 | 0 | |
130 | 0 | if (Preferences::GetBool("alerts.showFavicons")) { |
131 | 0 | nsresult rv = ShowWithIconBackend(aBackend, aAlert, aAlertListener); |
132 | 0 | if (NS_SUCCEEDED(rv)) { |
133 | 0 | return rv; |
134 | 0 | } |
135 | 0 | } |
136 | 0 | |
137 | 0 | // If favicons are disabled, or the backend doesn't support them, show the |
138 | 0 | // alert without one. |
139 | 0 | return aBackend->ShowAlert(aAlert, aAlertListener); |
140 | 0 | } |
141 | | |
142 | | } // anonymous namespace |
143 | | |
144 | | NS_IMPL_ISUPPORTS(nsAlertsService, nsIAlertsService, nsIAlertsDoNotDisturb) |
145 | | |
146 | | nsAlertsService::nsAlertsService() : |
147 | | mBackend(nullptr) |
148 | 0 | { |
149 | 0 | mBackend = do_GetService(NS_SYSTEMALERTSERVICE_CONTRACTID); |
150 | 0 | } |
151 | | |
152 | | nsAlertsService::~nsAlertsService() |
153 | 0 | {} |
154 | | |
155 | | bool nsAlertsService::ShouldShowAlert() |
156 | 0 | { |
157 | 0 | bool result = true; |
158 | 0 |
|
159 | | #ifdef XP_WIN |
160 | | QUERY_USER_NOTIFICATION_STATE qstate; |
161 | | if (SUCCEEDED(SHQueryUserNotificationState(&qstate))) { |
162 | | if (qstate != QUNS_ACCEPTS_NOTIFICATIONS) { |
163 | | result = false; |
164 | | } |
165 | | } |
166 | | #endif |
167 | |
|
168 | 0 | return result; |
169 | 0 | } |
170 | | |
171 | | bool nsAlertsService::ShouldUseSystemBackend() |
172 | 0 | { |
173 | 0 | if (!mBackend) { |
174 | 0 | return false; |
175 | 0 | } |
176 | 0 | static bool sAlertsUseSystemBackend; |
177 | 0 | static bool sAlertsUseSystemBackendCached = false; |
178 | 0 | if (!sAlertsUseSystemBackendCached) { |
179 | 0 | sAlertsUseSystemBackendCached = true; |
180 | 0 | Preferences::AddBoolVarCache(&sAlertsUseSystemBackend, "alerts.useSystemBackend", true); |
181 | 0 | } |
182 | 0 | return sAlertsUseSystemBackend; |
183 | 0 | } |
184 | | |
185 | | NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle, |
186 | | const nsAString & aAlertText, bool aAlertTextClickable, |
187 | | const nsAString & aAlertCookie, |
188 | | nsIObserver * aAlertListener, |
189 | | const nsAString & aAlertName, |
190 | | const nsAString & aBidi, |
191 | | const nsAString & aLang, |
192 | | const nsAString & aData, |
193 | | nsIPrincipal * aPrincipal, |
194 | | bool aInPrivateBrowsing, |
195 | | bool aRequireInteraction) |
196 | 0 | { |
197 | 0 | nsCOMPtr<nsIAlertNotification> alert = |
198 | 0 | do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID); |
199 | 0 | NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE); |
200 | 0 | nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle, |
201 | 0 | aAlertText, aAlertTextClickable, |
202 | 0 | aAlertCookie, aBidi, aLang, aData, |
203 | 0 | aPrincipal, aInPrivateBrowsing, |
204 | 0 | aRequireInteraction); |
205 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
206 | 0 | return ShowAlert(alert, aAlertListener); |
207 | 0 | } |
208 | | |
209 | | |
210 | | NS_IMETHODIMP nsAlertsService::ShowAlert(nsIAlertNotification * aAlert, |
211 | | nsIObserver * aAlertListener) |
212 | 0 | { |
213 | 0 | return ShowPersistentNotification(EmptyString(), aAlert, aAlertListener); |
214 | 0 | } |
215 | | |
216 | | NS_IMETHODIMP nsAlertsService::ShowPersistentNotification(const nsAString & aPersistentData, |
217 | | nsIAlertNotification * aAlert, |
218 | | nsIObserver * aAlertListener) |
219 | 0 | { |
220 | 0 | NS_ENSURE_ARG(aAlert); |
221 | 0 |
|
222 | 0 | nsAutoString cookie; |
223 | 0 | nsresult rv = aAlert->GetCookie(cookie); |
224 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
225 | 0 |
|
226 | 0 | if (XRE_IsContentProcess()) { |
227 | 0 | ContentChild* cpc = ContentChild::GetSingleton(); |
228 | 0 |
|
229 | 0 | if (aAlertListener) |
230 | 0 | cpc->AddRemoteAlertObserver(cookie, aAlertListener); |
231 | 0 |
|
232 | 0 | cpc->SendShowAlert(aAlert); |
233 | 0 | return NS_OK; |
234 | 0 | } |
235 | 0 |
|
236 | 0 | // Check if there is an optional service that handles system-level notifications |
237 | 0 | if (ShouldUseSystemBackend()) { |
238 | 0 | rv = ShowWithBackend(mBackend, aAlert, aAlertListener, aPersistentData); |
239 | 0 | if (NS_SUCCEEDED(rv)) { |
240 | 0 | return rv; |
241 | 0 | } |
242 | 0 | // If the system backend failed to show the alert, clear the backend and |
243 | 0 | // retry with XUL notifications. Future alerts will always use XUL. |
244 | 0 | mBackend = nullptr; |
245 | 0 | } |
246 | 0 |
|
247 | 0 | if (!ShouldShowAlert()) { |
248 | 0 | // Do not display the alert. Instead call alertfinished and get out. |
249 | 0 | if (aAlertListener) |
250 | 0 | aAlertListener->Observe(nullptr, "alertfinished", cookie.get()); |
251 | 0 | return NS_OK; |
252 | 0 | } |
253 | 0 |
|
254 | 0 | // Use XUL notifications as a fallback if above methods have failed. |
255 | 0 | nsCOMPtr<nsIAlertsService> xulBackend(nsXULAlerts::GetInstance()); |
256 | 0 | NS_ENSURE_TRUE(xulBackend, NS_ERROR_FAILURE); |
257 | 0 | return ShowWithBackend(xulBackend, aAlert, aAlertListener, aPersistentData); |
258 | 0 | } |
259 | | |
260 | | NS_IMETHODIMP nsAlertsService::CloseAlert(const nsAString& aAlertName, |
261 | | nsIPrincipal* aPrincipal) |
262 | 0 | { |
263 | 0 | if (XRE_IsContentProcess()) { |
264 | 0 | ContentChild* cpc = ContentChild::GetSingleton(); |
265 | 0 | cpc->SendCloseAlert(nsAutoString(aAlertName), IPC::Principal(aPrincipal)); |
266 | 0 | return NS_OK; |
267 | 0 | } |
268 | 0 | |
269 | 0 | nsresult rv; |
270 | 0 | // Try the system notification service. |
271 | 0 | if (ShouldUseSystemBackend()) { |
272 | 0 | rv = mBackend->CloseAlert(aAlertName, aPrincipal); |
273 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
274 | 0 | // If the system backend failed to close the alert, fall back to XUL for |
275 | 0 | // future alerts. |
276 | 0 | mBackend = nullptr; |
277 | 0 | } |
278 | 0 | } else { |
279 | 0 | nsCOMPtr<nsIAlertsService> xulBackend(nsXULAlerts::GetInstance()); |
280 | 0 | NS_ENSURE_TRUE(xulBackend, NS_ERROR_FAILURE); |
281 | 0 | rv = xulBackend->CloseAlert(aAlertName, aPrincipal); |
282 | 0 | } |
283 | 0 | return rv; |
284 | 0 | } |
285 | | |
286 | | |
287 | | // nsIAlertsDoNotDisturb |
288 | | NS_IMETHODIMP nsAlertsService::GetManualDoNotDisturb(bool* aRetVal) |
289 | 0 | { |
290 | | #ifdef MOZ_WIDGET_ANDROID |
291 | | return NS_ERROR_NOT_IMPLEMENTED; |
292 | | #else |
293 | | nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(GetDNDBackend()); |
294 | 0 | NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED); |
295 | 0 | return alertsDND->GetManualDoNotDisturb(aRetVal); |
296 | 0 | #endif |
297 | 0 | } |
298 | | |
299 | | NS_IMETHODIMP nsAlertsService::SetManualDoNotDisturb(bool aDoNotDisturb) |
300 | 0 | { |
301 | | #ifdef MOZ_WIDGET_ANDROID |
302 | | return NS_ERROR_NOT_IMPLEMENTED; |
303 | | #else |
304 | | nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(GetDNDBackend()); |
305 | 0 | NS_ENSURE_TRUE(alertsDND, NS_ERROR_NOT_IMPLEMENTED); |
306 | 0 |
|
307 | 0 | nsresult rv = alertsDND->SetManualDoNotDisturb(aDoNotDisturb); |
308 | 0 | if (NS_SUCCEEDED(rv)) { |
309 | 0 | Telemetry::Accumulate(Telemetry::ALERTS_SERVICE_DND_ENABLED, 1); |
310 | 0 | } |
311 | 0 | return rv; |
312 | 0 | #endif |
313 | 0 | } |
314 | | |
315 | | already_AddRefed<nsIAlertsDoNotDisturb> |
316 | | nsAlertsService::GetDNDBackend() |
317 | 0 | { |
318 | 0 | nsCOMPtr<nsIAlertsService> backend; |
319 | 0 | // Try the system notification service. |
320 | 0 | if (ShouldUseSystemBackend()) { |
321 | 0 | backend = mBackend; |
322 | 0 | } |
323 | 0 | if (!backend) { |
324 | 0 | backend = nsXULAlerts::GetInstance(); |
325 | 0 | } |
326 | 0 |
|
327 | 0 | nsCOMPtr<nsIAlertsDoNotDisturb> alertsDND(do_QueryInterface(backend)); |
328 | 0 | return alertsDND.forget(); |
329 | 0 | } |