Coverage Report

Created: 2018-09-25 14:53

/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
}