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