Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/alerts/nsXULAlerts.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 "nsXULAlerts.h"
7
8
#include "nsArray.h"
9
#include "nsComponentManagerUtils.h"
10
#include "nsCOMPtr.h"
11
#include "mozilla/ClearOnShutdown.h"
12
#include "mozilla/LookAndFeel.h"
13
#include "mozilla/dom/Notification.h"
14
#include "mozilla/Unused.h"
15
#include "nsIServiceManager.h"
16
#include "nsISupportsPrimitives.h"
17
#include "nsPIDOMWindow.h"
18
#include "nsIWindowWatcher.h"
19
20
using namespace mozilla;
21
using mozilla::dom::NotificationTelemetryService;
22
23
0
#define ALERT_CHROME_URL "chrome://global/content/alerts/alert.xul"
24
25
namespace {
26
StaticRefPtr<nsXULAlerts> gXULAlerts;
27
} // anonymous namespace
28
29
NS_IMPL_CYCLE_COLLECTION(nsXULAlertObserver, mAlertWindow)
30
31
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULAlertObserver)
32
0
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
33
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
34
0
NS_INTERFACE_MAP_END
35
36
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULAlertObserver)
37
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULAlertObserver)
38
39
NS_IMETHODIMP
40
nsXULAlertObserver::Observe(nsISupports* aSubject, const char* aTopic,
41
                            const char16_t* aData)
42
0
{
43
0
  if (!strcmp("alertfinished", aTopic)) {
44
0
    mozIDOMWindowProxy* currentAlert = mXULAlerts->mNamedWindows.GetWeak(mAlertName);
45
0
    // The window in mNamedWindows might be a replacement, thus it should only
46
0
    // be removed if it is the same window that is associated with this listener.
47
0
    if (currentAlert == mAlertWindow) {
48
0
      mXULAlerts->mNamedWindows.Remove(mAlertName);
49
0
50
0
      if (mIsPersistent) {
51
0
        mXULAlerts->PersistentAlertFinished();
52
0
      }
53
0
    }
54
0
  }
55
0
56
0
  nsresult rv = NS_OK;
57
0
  if (mObserver) {
58
0
    rv = mObserver->Observe(aSubject, aTopic, aData);
59
0
  }
60
0
  return rv;
61
0
}
62
63
// We don't cycle collect nsXULAlerts since gXULAlerts will keep the instance
64
// alive till shutdown anyway.
65
NS_IMPL_ISUPPORTS(nsXULAlerts, nsIAlertsService, nsIAlertsDoNotDisturb, nsIAlertsIconURI)
66
67
/* static */ already_AddRefed<nsXULAlerts>
68
nsXULAlerts::GetInstance()
69
0
{
70
0
  // Gecko on Android does not fully support XUL windows.
71
0
#ifndef MOZ_WIDGET_ANDROID
72
0
  if (!gXULAlerts) {
73
0
    gXULAlerts = new nsXULAlerts();
74
0
    ClearOnShutdown(&gXULAlerts);
75
0
  }
76
0
#endif // MOZ_WIDGET_ANDROID
77
0
  RefPtr<nsXULAlerts> instance = gXULAlerts.get();
78
0
  return instance.forget();
79
0
}
80
81
void
82
nsXULAlerts::PersistentAlertFinished()
83
0
{
84
0
  MOZ_ASSERT(mPersistentAlertCount);
85
0
  mPersistentAlertCount--;
86
0
87
0
  // Show next pending persistent alert if any.
88
0
  if (!mPendingPersistentAlerts.IsEmpty()) {
89
0
    ShowAlertWithIconURI(mPendingPersistentAlerts[0].mAlert,
90
0
                         mPendingPersistentAlerts[0].mListener,
91
0
                         nullptr);
92
0
    mPendingPersistentAlerts.RemoveElementAt(0);
93
0
  }
94
0
}
95
96
NS_IMETHODIMP
97
nsXULAlerts::ShowAlertNotification(const nsAString& aImageUrl, const nsAString& aAlertTitle,
98
                                   const nsAString& aAlertText, bool aAlertTextClickable,
99
                                   const nsAString& aAlertCookie, nsIObserver* aAlertListener,
100
                                   const nsAString& aAlertName, const nsAString& aBidi,
101
                                   const nsAString& aLang, const nsAString& aData,
102
                                   nsIPrincipal* aPrincipal, bool aInPrivateBrowsing,
103
                                   bool aRequireInteraction)
104
0
{
105
0
  nsCOMPtr<nsIAlertNotification> alert =
106
0
    do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID);
107
0
  NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE);
108
0
  nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle,
109
0
                            aAlertText, aAlertTextClickable,
110
0
                            aAlertCookie, aBidi, aLang, aData,
111
0
                            aPrincipal, aInPrivateBrowsing,
112
0
                            aRequireInteraction);
113
0
  NS_ENSURE_SUCCESS(rv, rv);
114
0
  return ShowAlert(alert, aAlertListener);
115
0
}
116
117
NS_IMETHODIMP
118
nsXULAlerts::ShowPersistentNotification(const nsAString& aPersistentData,
119
                                        nsIAlertNotification* aAlert,
120
                                        nsIObserver* aAlertListener)
121
0
{
122
0
  return ShowAlert(aAlert, aAlertListener);
123
0
}
124
125
NS_IMETHODIMP
126
nsXULAlerts::ShowAlert(nsIAlertNotification* aAlert,
127
                       nsIObserver* aAlertListener)
128
0
{
129
0
  nsAutoString name;
130
0
  nsresult rv = aAlert->GetName(name);
131
0
  NS_ENSURE_SUCCESS(rv, rv);
132
0
133
0
  // If there is a pending alert with the same name in the list of
134
0
  // pending alerts, replace it.
135
0
  if (!mPendingPersistentAlerts.IsEmpty()) {
136
0
    for (uint32_t i = 0; i < mPendingPersistentAlerts.Length(); i++) {
137
0
      nsAutoString pendingAlertName;
138
0
      nsCOMPtr<nsIAlertNotification> pendingAlert = mPendingPersistentAlerts[i].mAlert;
139
0
      rv = pendingAlert->GetName(pendingAlertName);
140
0
      NS_ENSURE_SUCCESS(rv, rv);
141
0
142
0
      if (pendingAlertName.Equals(name)) {
143
0
        nsAutoString cookie;
144
0
        rv = pendingAlert->GetCookie(cookie);
145
0
        NS_ENSURE_SUCCESS(rv, rv);
146
0
147
0
        if (mPendingPersistentAlerts[i].mListener) {
148
0
          rv = mPendingPersistentAlerts[i].mListener->Observe(nullptr, "alertfinished", cookie.get());
149
0
          NS_ENSURE_SUCCESS(rv, rv);
150
0
        }
151
0
152
0
        mPendingPersistentAlerts[i].Init(aAlert, aAlertListener);
153
0
        return NS_OK;
154
0
      }
155
0
    }
156
0
  }
157
0
158
0
  bool requireInteraction;
159
0
  rv = aAlert->GetRequireInteraction(&requireInteraction);
160
0
  NS_ENSURE_SUCCESS(rv, rv);
161
0
162
0
  if (requireInteraction &&
163
0
      !mNamedWindows.Contains(name) &&
164
0
      static_cast<int32_t>(mPersistentAlertCount) >=
165
0
        Preferences::GetInt("dom.webnotifications.requireinteraction.count", 0)) {
166
0
    PendingAlert* pa = mPendingPersistentAlerts.AppendElement();
167
0
    pa->Init(aAlert, aAlertListener);
168
0
    return NS_OK;
169
0
  } else {
170
0
    return ShowAlertWithIconURI(aAlert, aAlertListener, nullptr);
171
0
  }
172
0
}
173
174
NS_IMETHODIMP
175
nsXULAlerts::ShowAlertWithIconURI(nsIAlertNotification* aAlert,
176
                                  nsIObserver* aAlertListener,
177
                                  nsIURI* aIconURI)
178
0
{
179
0
  bool inPrivateBrowsing;
180
0
  nsresult rv = aAlert->GetInPrivateBrowsing(&inPrivateBrowsing);
181
0
  NS_ENSURE_SUCCESS(rv, rv);
182
0
183
0
  nsAutoString cookie;
184
0
  rv = aAlert->GetCookie(cookie);
185
0
  NS_ENSURE_SUCCESS(rv, rv);
186
0
187
0
  if (mDoNotDisturb) {
188
0
    if (aAlertListener)
189
0
      aAlertListener->Observe(nullptr, "alertfinished", cookie.get());
190
0
    return NS_OK;
191
0
  }
192
0
193
0
  nsAutoString name;
194
0
  rv = aAlert->GetName(name);
195
0
  NS_ENSURE_SUCCESS(rv, rv);
196
0
197
0
  nsAutoString imageUrl;
198
0
  rv = aAlert->GetImageURL(imageUrl);
199
0
  NS_ENSURE_SUCCESS(rv, rv);
200
0
201
0
  nsAutoString title;
202
0
  rv = aAlert->GetTitle(title);
203
0
  NS_ENSURE_SUCCESS(rv, rv);
204
0
205
0
  nsAutoString text;
206
0
  rv = aAlert->GetText(text);
207
0
  NS_ENSURE_SUCCESS(rv, rv);
208
0
209
0
  bool textClickable;
210
0
  rv = aAlert->GetTextClickable(&textClickable);
211
0
  NS_ENSURE_SUCCESS(rv, rv);
212
0
213
0
  nsAutoString bidi;
214
0
  rv = aAlert->GetDir(bidi);
215
0
  NS_ENSURE_SUCCESS(rv, rv);
216
0
217
0
  nsAutoString lang;
218
0
  rv = aAlert->GetLang(lang);
219
0
  NS_ENSURE_SUCCESS(rv, rv);
220
0
221
0
  nsAutoString source;
222
0
  rv = aAlert->GetSource(source);
223
0
  NS_ENSURE_SUCCESS(rv, rv);
224
0
225
0
  bool requireInteraction;
226
0
  rv = aAlert->GetRequireInteraction(&requireInteraction);
227
0
  NS_ENSURE_SUCCESS(rv, rv);
228
0
229
0
  nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
230
0
231
0
  nsCOMPtr<nsIMutableArray> argsArray = nsArray::Create();
232
0
233
0
  // create scriptable versions of our strings that we can store in our nsIMutableArray....
234
0
  nsCOMPtr<nsISupportsString> scriptableImageUrl (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
235
0
  NS_ENSURE_TRUE(scriptableImageUrl, NS_ERROR_FAILURE);
236
0
237
0
  scriptableImageUrl->SetData(imageUrl);
238
0
  rv = argsArray->AppendElement(scriptableImageUrl);
239
0
  NS_ENSURE_SUCCESS(rv, rv);
240
0
241
0
  nsCOMPtr<nsISupportsString> scriptableAlertTitle (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
242
0
  NS_ENSURE_TRUE(scriptableAlertTitle, NS_ERROR_FAILURE);
243
0
244
0
  scriptableAlertTitle->SetData(title);
245
0
  rv = argsArray->AppendElement(scriptableAlertTitle);
246
0
  NS_ENSURE_SUCCESS(rv, rv);
247
0
248
0
  nsCOMPtr<nsISupportsString> scriptableAlertText (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
249
0
  NS_ENSURE_TRUE(scriptableAlertText, NS_ERROR_FAILURE);
250
0
251
0
  scriptableAlertText->SetData(text);
252
0
  rv = argsArray->AppendElement(scriptableAlertText);
253
0
  NS_ENSURE_SUCCESS(rv, rv);
254
0
255
0
  nsCOMPtr<nsISupportsPRBool> scriptableIsClickable (do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID));
256
0
  NS_ENSURE_TRUE(scriptableIsClickable, NS_ERROR_FAILURE);
257
0
258
0
  scriptableIsClickable->SetData(textClickable);
259
0
  rv = argsArray->AppendElement(scriptableIsClickable);
260
0
  NS_ENSURE_SUCCESS(rv, rv);
261
0
262
0
  nsCOMPtr<nsISupportsString> scriptableAlertCookie (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
263
0
  NS_ENSURE_TRUE(scriptableAlertCookie, NS_ERROR_FAILURE);
264
0
265
0
  scriptableAlertCookie->SetData(cookie);
266
0
  rv = argsArray->AppendElement(scriptableAlertCookie);
267
0
  NS_ENSURE_SUCCESS(rv, rv);
268
0
269
0
  nsCOMPtr<nsISupportsPRInt32> scriptableOrigin (do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID));
270
0
  NS_ENSURE_TRUE(scriptableOrigin, NS_ERROR_FAILURE);
271
0
272
0
  int32_t origin =
273
0
    LookAndFeel::GetInt(LookAndFeel::eIntID_AlertNotificationOrigin);
274
0
  scriptableOrigin->SetData(origin);
275
0
276
0
  rv = argsArray->AppendElement(scriptableOrigin);
277
0
  NS_ENSURE_SUCCESS(rv, rv);
278
0
279
0
  nsCOMPtr<nsISupportsString> scriptableBidi (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
280
0
  NS_ENSURE_TRUE(scriptableBidi, NS_ERROR_FAILURE);
281
0
282
0
  scriptableBidi->SetData(bidi);
283
0
  rv = argsArray->AppendElement(scriptableBidi);
284
0
  NS_ENSURE_SUCCESS(rv, rv);
285
0
286
0
  nsCOMPtr<nsISupportsString> scriptableLang (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
287
0
  NS_ENSURE_TRUE(scriptableLang, NS_ERROR_FAILURE);
288
0
289
0
  scriptableLang->SetData(lang);
290
0
  rv = argsArray->AppendElement(scriptableLang);
291
0
  NS_ENSURE_SUCCESS(rv, rv);
292
0
293
0
  nsCOMPtr<nsISupportsPRBool> scriptableRequireInteraction (do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID));
294
0
  NS_ENSURE_TRUE(scriptableRequireInteraction, NS_ERROR_FAILURE);
295
0
296
0
  scriptableRequireInteraction->SetData(requireInteraction);
297
0
  rv = argsArray->AppendElement(scriptableRequireInteraction);
298
0
  NS_ENSURE_SUCCESS(rv, rv);
299
0
300
0
  // Alerts with the same name should replace the old alert in the same position.
301
0
  // Provide the new alert window with a pointer to the replaced window so that
302
0
  // it may take the same position.
303
0
  nsCOMPtr<nsISupportsInterfacePointer> replacedWindow = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
304
0
  NS_ENSURE_TRUE(replacedWindow, NS_ERROR_FAILURE);
305
0
  mozIDOMWindowProxy* previousAlert = mNamedWindows.GetWeak(name);
306
0
  replacedWindow->SetData(previousAlert);
307
0
  replacedWindow->SetDataIID(&NS_GET_IID(mozIDOMWindowProxy));
308
0
  rv = argsArray->AppendElement(replacedWindow);
309
0
  NS_ENSURE_SUCCESS(rv, rv);
310
0
311
0
  if (requireInteraction) {
312
0
    mPersistentAlertCount++;
313
0
  }
314
0
315
0
  // Add an observer (that wraps aAlertListener) to remove the window from
316
0
  // mNamedWindows when it is closed.
317
0
  nsCOMPtr<nsISupportsInterfacePointer> ifptr = do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
318
0
  NS_ENSURE_SUCCESS(rv, rv);
319
0
  RefPtr<nsXULAlertObserver> alertObserver = new nsXULAlertObserver(this, name, aAlertListener, requireInteraction);
320
0
  nsCOMPtr<nsISupports> iSupports(do_QueryInterface(alertObserver));
321
0
  ifptr->SetData(iSupports);
322
0
  ifptr->SetDataIID(&NS_GET_IID(nsIObserver));
323
0
  rv = argsArray->AppendElement(ifptr);
324
0
  NS_ENSURE_SUCCESS(rv, rv);
325
0
326
0
  // The source contains the host and port of the site that sent the
327
0
  // notification. It is empty for system alerts.
328
0
  nsCOMPtr<nsISupportsString> scriptableAlertSource (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
329
0
  NS_ENSURE_TRUE(scriptableAlertSource, NS_ERROR_FAILURE);
330
0
  scriptableAlertSource->SetData(source);
331
0
  rv = argsArray->AppendElement(scriptableAlertSource);
332
0
  NS_ENSURE_SUCCESS(rv, rv);
333
0
334
0
  nsCOMPtr<nsISupportsCString> scriptableIconURL (do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
335
0
  NS_ENSURE_TRUE(scriptableIconURL, NS_ERROR_FAILURE);
336
0
  if (aIconURI) {
337
0
    nsAutoCString iconURL;
338
0
    rv = aIconURI->GetSpec(iconURL);
339
0
    NS_ENSURE_SUCCESS(rv, rv);
340
0
    scriptableIconURL->SetData(iconURL);
341
0
  }
342
0
  rv = argsArray->AppendElement(scriptableIconURL);
343
0
  NS_ENSURE_SUCCESS(rv, rv);
344
0
345
0
  nsCOMPtr<mozIDOMWindowProxy> newWindow;
346
0
  nsAutoCString features("chrome,dialog=yes,titlebar=no,popup=yes");
347
0
  if (inPrivateBrowsing) {
348
0
    features.AppendLiteral(",private");
349
0
  }
350
0
  rv = wwatch->OpenWindow(nullptr, ALERT_CHROME_URL, "_blank", features.get(),
351
0
                          argsArray, getter_AddRefs(newWindow));
352
0
  NS_ENSURE_SUCCESS(rv, rv);
353
0
354
0
  mNamedWindows.Put(name, newWindow);
355
0
  alertObserver->SetAlertWindow(newWindow);
356
0
357
0
  return NS_OK;
358
0
}
359
360
NS_IMETHODIMP
361
nsXULAlerts::SetManualDoNotDisturb(bool aDoNotDisturb)
362
0
{
363
0
  mDoNotDisturb = aDoNotDisturb;
364
0
  return NS_OK;
365
0
}
366
367
NS_IMETHODIMP
368
nsXULAlerts::GetManualDoNotDisturb(bool* aRetVal)
369
0
{
370
0
  *aRetVal = mDoNotDisturb;
371
0
  return NS_OK;
372
0
}
373
374
NS_IMETHODIMP
375
nsXULAlerts::CloseAlert(const nsAString& aAlertName,
376
                        nsIPrincipal* aPrincipal)
377
0
{
378
0
  mozIDOMWindowProxy* alert = mNamedWindows.GetWeak(aAlertName);
379
0
  if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = nsPIDOMWindowOuter::From(alert)) {
380
0
    domWindow->DispatchCustomEvent(NS_LITERAL_STRING("XULAlertClose"));
381
0
  }
382
0
  return NS_OK;
383
0
}
384