Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/push/PushNotifier.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "PushNotifier.h"
8
9
#include "nsContentUtils.h"
10
#include "nsCOMPtr.h"
11
#include "nsICategoryManager.h"
12
#include "nsIXULRuntime.h"
13
#include "nsNetUtil.h"
14
#include "nsXPCOM.h"
15
#include "mozilla/dom/ServiceWorkerManager.h"
16
17
#include "mozilla/Services.h"
18
#include "mozilla/Unused.h"
19
20
#include "mozilla/dom/BodyUtil.h"
21
#include "mozilla/dom/ContentChild.h"
22
#include "mozilla/dom/ContentParent.h"
23
24
namespace mozilla {
25
namespace dom {
26
27
PushNotifier::PushNotifier()
28
0
{}
29
30
PushNotifier::~PushNotifier()
31
0
{}
32
33
NS_IMPL_CYCLE_COLLECTION_0(PushNotifier)
34
35
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushNotifier)
36
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushNotifier)
37
0
  NS_INTERFACE_MAP_ENTRY(nsIPushNotifier)
38
0
NS_INTERFACE_MAP_END
39
40
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushNotifier)
41
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushNotifier)
42
43
NS_IMETHODIMP
44
PushNotifier::NotifyPushWithData(const nsACString& aScope,
45
                                 nsIPrincipal* aPrincipal,
46
                                 const nsAString& aMessageId,
47
                                 uint32_t aDataLen, uint8_t* aData)
48
0
{
49
0
  NS_ENSURE_ARG(aPrincipal);
50
0
  nsTArray<uint8_t> data;
51
0
  if (!data.SetCapacity(aDataLen, fallible)) {
52
0
    return NS_ERROR_OUT_OF_MEMORY;
53
0
  }
54
0
  if (!data.InsertElementsAt(0, aData, aDataLen, fallible)) {
55
0
    return NS_ERROR_OUT_OF_MEMORY;
56
0
  }
57
0
  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(data));
58
0
  return Dispatch(dispatcher);
59
0
}
60
61
NS_IMETHODIMP
62
PushNotifier::NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
63
                         const nsAString& aMessageId)
64
0
{
65
0
  NS_ENSURE_ARG(aPrincipal);
66
0
  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
67
0
  return Dispatch(dispatcher);
68
0
}
69
70
NS_IMETHODIMP
71
PushNotifier::NotifySubscriptionChange(const nsACString& aScope,
72
                                       nsIPrincipal* aPrincipal)
73
0
{
74
0
  NS_ENSURE_ARG(aPrincipal);
75
0
  PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
76
0
  return Dispatch(dispatcher);
77
0
}
78
79
NS_IMETHODIMP
80
PushNotifier::NotifySubscriptionModified(const nsACString& aScope,
81
                                         nsIPrincipal* aPrincipal)
82
0
{
83
0
  NS_ENSURE_ARG(aPrincipal);
84
0
  PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
85
0
  return Dispatch(dispatcher);
86
0
}
87
88
NS_IMETHODIMP
89
PushNotifier::NotifyError(const nsACString& aScope, nsIPrincipal* aPrincipal,
90
                          const nsAString& aMessage, uint32_t aFlags)
91
0
{
92
0
  NS_ENSURE_ARG(aPrincipal);
93
0
  PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
94
0
  return Dispatch(dispatcher);
95
0
}
96
97
nsresult
98
PushNotifier::Dispatch(PushDispatcher& aDispatcher)
99
0
{
100
0
  if (XRE_IsParentProcess()) {
101
0
    // Always notify XPCOM observers in the parent process.
102
0
    Unused << NS_WARN_IF(NS_FAILED(aDispatcher.NotifyObservers()));
103
0
104
0
    nsTArray<ContentParent*> contentActors;
105
0
    ContentParent::GetAll(contentActors);
106
0
    if (!contentActors.IsEmpty() && !ServiceWorkerParentInterceptEnabled()) {
107
0
      // At least one content process is active, so e10s must be enabled.
108
0
      // Broadcast a message to notify observers and service workers.
109
0
      for (uint32_t i = 0; i < contentActors.Length(); ++i) {
110
0
        // We need to filter based on process type, only "web" AKA the default
111
0
        // remote type is acceptable.
112
0
        if (!contentActors[i]->GetRemoteType().EqualsLiteral(
113
0
               DEFAULT_REMOTE_TYPE)) {
114
0
          continue;
115
0
        }
116
0
117
0
        // Ensure that the content actor has the permissions avaliable for the
118
0
        // principal the push is being sent for before sending the push message
119
0
        // down.
120
0
        Unused << contentActors[i]->
121
0
          TransmitPermissionsForPrincipal(aDispatcher.GetPrincipal());
122
0
        if (aDispatcher.SendToChild(contentActors[i])) {
123
0
          // Only send the push message to the first content process to avoid
124
0
          // multiple SWs showing the same notification. See bug 1300112.
125
0
          break;
126
0
        }
127
0
      }
128
0
      return NS_OK;
129
0
    }
130
0
131
0
    if (BrowserTabsRemoteAutostart() && !ServiceWorkerParentInterceptEnabled()) {
132
0
      // e10s is enabled, but no content processes are active.
133
0
      return aDispatcher.HandleNoChildProcesses();
134
0
    }
135
0
136
0
    // e10s is disabled; notify workers in the parent.
137
0
    return aDispatcher.NotifyWorkers();
138
0
  }
139
0
140
0
  // Otherwise, we're in the content process, so e10s must be enabled. Notify
141
0
  // observers and workers, then send a message to notify observers in the
142
0
  // parent.
143
0
  MOZ_ASSERT(XRE_IsContentProcess());
144
0
145
0
  nsresult rv = aDispatcher.NotifyObserversAndWorkers();
146
0
147
0
  ContentChild* parentActor = ContentChild::GetSingleton();
148
0
  if (!NS_WARN_IF(!parentActor)) {
149
0
    Unused << NS_WARN_IF(!aDispatcher.SendToParent(parentActor));
150
0
  }
151
0
152
0
  return rv;
153
0
}
154
155
PushData::PushData(const nsTArray<uint8_t>& aData)
156
  : mData(aData)
157
0
{}
158
159
PushData::~PushData()
160
0
{}
161
162
NS_IMPL_CYCLE_COLLECTION_0(PushData)
163
164
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushData)
165
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushData)
166
0
  NS_INTERFACE_MAP_ENTRY(nsIPushData)
167
0
NS_INTERFACE_MAP_END
168
169
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushData)
170
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushData)
171
172
nsresult
173
PushData::EnsureDecodedText()
174
0
{
175
0
  if (mData.IsEmpty() || !mDecodedText.IsEmpty()) {
176
0
    return NS_OK;
177
0
  }
178
0
  nsresult rv = BodyUtil::ConsumeText(
179
0
    mData.Length(),
180
0
    reinterpret_cast<uint8_t*>(mData.Elements()),
181
0
    mDecodedText
182
0
  );
183
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
184
0
    mDecodedText.Truncate();
185
0
    return rv;
186
0
  }
187
0
  return NS_OK;
188
0
}
189
190
NS_IMETHODIMP
191
PushData::Text(nsAString& aText)
192
0
{
193
0
  nsresult rv = EnsureDecodedText();
194
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
195
0
    return rv;
196
0
  }
197
0
  aText = mDecodedText;
198
0
  return NS_OK;
199
0
}
200
201
NS_IMETHODIMP
202
PushData::Json(JSContext* aCx,
203
               JS::MutableHandle<JS::Value> aResult)
204
0
{
205
0
  nsresult rv = EnsureDecodedText();
206
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
207
0
    return rv;
208
0
  }
209
0
  ErrorResult error;
210
0
  BodyUtil::ConsumeJson(aCx, aResult, mDecodedText, error);
211
0
  return error.StealNSResult();
212
0
}
213
214
NS_IMETHODIMP
215
PushData::Binary(uint32_t* aDataLen, uint8_t** aData)
216
0
{
217
0
  NS_ENSURE_ARG_POINTER(aDataLen);
218
0
  NS_ENSURE_ARG_POINTER(aData);
219
0
220
0
  *aData = nullptr;
221
0
  if (mData.IsEmpty()) {
222
0
    *aDataLen = 0;
223
0
    return NS_OK;
224
0
  }
225
0
  uint32_t length = mData.Length();
226
0
  uint8_t* data = static_cast<uint8_t*>(moz_xmalloc(length * sizeof(uint8_t)));
227
0
  memcpy(data, mData.Elements(), length * sizeof(uint8_t));
228
0
  *aDataLen = length;
229
0
  *aData = data;
230
0
  return NS_OK;
231
0
}
232
233
PushMessage::PushMessage(nsIPrincipal* aPrincipal, nsIPushData* aData)
234
  : mPrincipal(aPrincipal)
235
  , mData(aData)
236
0
{}
237
238
PushMessage::~PushMessage()
239
0
{}
240
241
NS_IMPL_CYCLE_COLLECTION(PushMessage, mPrincipal, mData)
242
243
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushMessage)
244
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPushMessage)
245
0
  NS_INTERFACE_MAP_ENTRY(nsIPushMessage)
246
0
NS_INTERFACE_MAP_END
247
248
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushMessage)
249
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushMessage)
250
251
NS_IMETHODIMP
252
PushMessage::GetPrincipal(nsIPrincipal** aPrincipal)
253
0
{
254
0
  NS_ENSURE_ARG_POINTER(aPrincipal);
255
0
256
0
  nsCOMPtr<nsIPrincipal> principal = mPrincipal;
257
0
  principal.forget(aPrincipal);
258
0
  return NS_OK;
259
0
}
260
261
NS_IMETHODIMP
262
PushMessage::GetData(nsIPushData** aData)
263
0
{
264
0
  NS_ENSURE_ARG_POINTER(aData);
265
0
266
0
  nsCOMPtr<nsIPushData> data = mData;
267
0
  data.forget(aData);
268
0
  return NS_OK;
269
0
}
270
271
PushDispatcher::PushDispatcher(const nsACString& aScope,
272
                               nsIPrincipal* aPrincipal)
273
  : mScope(aScope)
274
  , mPrincipal(aPrincipal)
275
0
{}
276
277
PushDispatcher::~PushDispatcher()
278
0
{}
279
280
nsresult
281
PushDispatcher::HandleNoChildProcesses()
282
0
{
283
0
  return NS_OK;
284
0
}
285
286
nsresult
287
PushDispatcher::NotifyObserversAndWorkers()
288
0
{
289
0
  Unused << NS_WARN_IF(NS_FAILED(NotifyObservers()));
290
0
  return NotifyWorkers();
291
0
}
292
293
bool
294
PushDispatcher::ShouldNotifyWorkers()
295
0
{
296
0
  if (NS_WARN_IF(!mPrincipal)) {
297
0
    return false;
298
0
  }
299
0
  // System subscriptions use observer notifications instead of service worker
300
0
  // events. The `testing.notifyWorkers` pref disables worker events for
301
0
  // non-system subscriptions.
302
0
  return !nsContentUtils::IsSystemPrincipal(mPrincipal) &&
303
0
         Preferences::GetBool("dom.push.testing.notifyWorkers", true);
304
0
}
305
306
nsresult
307
PushDispatcher::DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
308
                                  const nsACString& aScope)
309
0
{
310
0
  nsCOMPtr<nsIObserverService> obsService =
311
0
    mozilla::services::GetObserverService();
312
0
  if (!obsService) {
313
0
    return NS_ERROR_FAILURE;
314
0
  }
315
0
  // If there's a service for this push category, make sure it is alive.
316
0
  nsCOMPtr<nsICategoryManager> catMan =
317
0
    do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
318
0
  if (catMan) {
319
0
    nsCString contractId;
320
0
    nsresult rv = catMan->GetCategoryEntry("push", mScope, contractId);
321
0
    if (NS_SUCCEEDED(rv)) {
322
0
      // Ensure the service is created - we don't need to do anything with
323
0
      // it though - we assume the service constructor attaches a listener.
324
0
      nsCOMPtr<nsISupports> service = do_GetService(contractId.get());
325
0
    }
326
0
  }
327
0
  return obsService->NotifyObservers(aSubject, aTopic,
328
0
                                     NS_ConvertUTF8toUTF16(mScope).get());
329
0
}
330
331
PushMessageDispatcher::PushMessageDispatcher(const nsACString& aScope,
332
                                             nsIPrincipal* aPrincipal,
333
                                             const nsAString& aMessageId,
334
                                             const Maybe<nsTArray<uint8_t>>& aData)
335
  : PushDispatcher(aScope, aPrincipal)
336
  , mMessageId(aMessageId)
337
  , mData(aData)
338
0
{}
339
340
PushMessageDispatcher::~PushMessageDispatcher()
341
0
{}
342
343
nsresult
344
PushMessageDispatcher::NotifyObservers()
345
0
{
346
0
  nsCOMPtr<nsIPushData> data;
347
0
  if (mData) {
348
0
    data = new PushData(mData.ref());
349
0
  }
350
0
  nsCOMPtr<nsIPushMessage> message = new PushMessage(mPrincipal, data);
351
0
  return DoNotifyObservers(message, OBSERVER_TOPIC_PUSH, mScope);
352
0
}
353
354
nsresult
355
PushMessageDispatcher::NotifyWorkers()
356
0
{
357
0
  if (!ShouldNotifyWorkers()) {
358
0
    return NS_OK;
359
0
  }
360
0
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
361
0
  if (!swm) {
362
0
    return NS_ERROR_FAILURE;
363
0
  }
364
0
  nsAutoCString originSuffix;
365
0
  nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
366
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
367
0
    return rv;
368
0
  }
369
0
  return swm->SendPushEvent(originSuffix, mScope, mMessageId, mData);
370
0
}
371
372
bool
373
PushMessageDispatcher::SendToParent(ContentChild* aParentActor)
374
0
{
375
0
  if (mData) {
376
0
    return aParentActor->SendNotifyPushObserversWithData(mScope,
377
0
                                                         IPC::Principal(mPrincipal),
378
0
                                                         mMessageId,
379
0
                                                         mData.ref());
380
0
  }
381
0
  return aParentActor->SendNotifyPushObservers(mScope,
382
0
                                               IPC::Principal(mPrincipal),
383
0
                                               mMessageId);
384
0
}
385
386
bool
387
PushMessageDispatcher::SendToChild(ContentParent* aContentActor)
388
0
{
389
0
  if (mData) {
390
0
    return aContentActor->SendPushWithData(mScope, IPC::Principal(mPrincipal),
391
0
                                           mMessageId, mData.ref());
392
0
  }
393
0
  return aContentActor->SendPush(mScope, IPC::Principal(mPrincipal),
394
0
                                 mMessageId);
395
0
}
396
397
PushSubscriptionChangeDispatcher::PushSubscriptionChangeDispatcher(const nsACString& aScope,
398
                                                                   nsIPrincipal* aPrincipal)
399
  : PushDispatcher(aScope, aPrincipal)
400
0
{}
401
402
PushSubscriptionChangeDispatcher::~PushSubscriptionChangeDispatcher()
403
{}
404
405
nsresult
406
PushSubscriptionChangeDispatcher::NotifyObservers()
407
0
{
408
0
  return DoNotifyObservers(mPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
409
0
                           mScope);
410
0
}
411
412
nsresult
413
PushSubscriptionChangeDispatcher::NotifyWorkers()
414
0
{
415
0
  if (!ShouldNotifyWorkers()) {
416
0
    return NS_OK;
417
0
  }
418
0
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
419
0
  if (!swm) {
420
0
    return NS_ERROR_FAILURE;
421
0
  }
422
0
  nsAutoCString originSuffix;
423
0
  nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
424
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
425
0
    return rv;
426
0
  }
427
0
  return swm->SendPushSubscriptionChangeEvent(originSuffix, mScope);
428
0
}
429
430
bool
431
PushSubscriptionChangeDispatcher::SendToParent(ContentChild* aParentActor)
432
0
{
433
0
  return aParentActor->SendNotifyPushSubscriptionChangeObservers(mScope,
434
0
                                                                 IPC::Principal(mPrincipal));
435
0
}
436
437
bool
438
PushSubscriptionChangeDispatcher::SendToChild(ContentParent* aContentActor)
439
0
{
440
0
  return aContentActor->SendPushSubscriptionChange(mScope,
441
0
                                                   IPC::Principal(mPrincipal));
442
0
}
443
444
PushSubscriptionModifiedDispatcher::PushSubscriptionModifiedDispatcher(const nsACString& aScope,
445
                                                                       nsIPrincipal* aPrincipal)
446
  : PushDispatcher(aScope, aPrincipal)
447
0
{}
448
449
PushSubscriptionModifiedDispatcher::~PushSubscriptionModifiedDispatcher()
450
{}
451
452
nsresult
453
PushSubscriptionModifiedDispatcher::NotifyObservers()
454
0
{
455
0
  return DoNotifyObservers(mPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED,
456
0
                           mScope);
457
0
}
458
459
nsresult
460
PushSubscriptionModifiedDispatcher::NotifyWorkers()
461
0
{
462
0
  return NS_OK;
463
0
}
464
465
bool
466
PushSubscriptionModifiedDispatcher::SendToParent(ContentChild* aParentActor)
467
0
{
468
0
  return aParentActor->SendNotifyPushSubscriptionModifiedObservers(mScope,
469
0
                                                                   IPC::Principal(mPrincipal));
470
0
}
471
472
bool
473
PushSubscriptionModifiedDispatcher::SendToChild(ContentParent* aContentActor)
474
0
{
475
0
  return aContentActor->SendNotifyPushSubscriptionModifiedObservers(mScope,
476
0
                                                                    IPC::Principal(mPrincipal));
477
0
}
478
479
PushErrorDispatcher::PushErrorDispatcher(const nsACString& aScope,
480
                                         nsIPrincipal* aPrincipal,
481
                                         const nsAString& aMessage,
482
                                         uint32_t aFlags)
483
  : PushDispatcher(aScope, aPrincipal)
484
  , mMessage(aMessage)
485
  , mFlags(aFlags)
486
0
{}
487
488
PushErrorDispatcher::~PushErrorDispatcher()
489
0
{}
490
491
nsresult
492
PushErrorDispatcher::NotifyObservers()
493
0
{
494
0
  return NS_OK;
495
0
}
496
497
nsresult
498
PushErrorDispatcher::NotifyWorkers()
499
0
{
500
0
  if (!ShouldNotifyWorkers()) {
501
0
    // For system subscriptions, log the error directly to the browser console.
502
0
    return nsContentUtils::ReportToConsoleNonLocalized(mMessage,
503
0
                                                       mFlags,
504
0
                                                       NS_LITERAL_CSTRING("Push"),
505
0
                                                       nullptr, /* aDocument */
506
0
                                                       nullptr, /* aURI */
507
0
                                                       EmptyString(), /* aLine */
508
0
                                                       0, /* aLineNumber */
509
0
                                                       0, /* aColumnNumber */
510
0
                                                       nsContentUtils::eOMIT_LOCATION);
511
0
  }
512
0
  // For service worker subscriptions, report the error to all clients.
513
0
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
514
0
  if (swm) {
515
0
    swm->ReportToAllClients(mScope,
516
0
                            mMessage,
517
0
                            NS_ConvertUTF8toUTF16(mScope), /* aFilename */
518
0
                            EmptyString(), /* aLine */
519
0
                            0, /* aLineNumber */
520
0
                            0, /* aColumnNumber */
521
0
                            mFlags);
522
0
  }
523
0
  return NS_OK;
524
0
}
525
526
bool
527
PushErrorDispatcher::SendToParent(ContentChild*)
528
0
{
529
0
  return true;
530
0
}
531
532
bool
533
PushErrorDispatcher::SendToChild(ContentParent* aContentActor)
534
0
{
535
0
  return aContentActor->SendPushError(mScope, IPC::Principal(mPrincipal),
536
0
                                      mMessage, mFlags);
537
0
}
538
539
nsresult
540
PushErrorDispatcher::HandleNoChildProcesses()
541
0
{
542
0
  // Report to the console if no content processes are active.
543
0
  nsCOMPtr<nsIURI> scopeURI;
544
0
  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope);
545
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
546
0
    return rv;
547
0
  }
548
0
  return nsContentUtils::ReportToConsoleNonLocalized(mMessage,
549
0
                                                     mFlags,
550
0
                                                     NS_LITERAL_CSTRING("Push"),
551
0
                                                     nullptr, /* aDocument */
552
0
                                                     scopeURI, /* aURI */
553
0
                                                     EmptyString(), /* aLine */
554
0
                                                     0, /* aLineNumber */
555
0
                                                     0, /* aColumnNumber */
556
0
                                                     nsContentUtils::eOMIT_LOCATION);
557
0
}
558
559
} // namespace dom
560
} // namespace mozilla