Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/mozilla/dom/Notification.h
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
#ifndef mozilla_dom_notification_h__
8
#define mozilla_dom_notification_h__
9
10
#include "mozilla/DOMEventTargetHelper.h"
11
#include "mozilla/UniquePtr.h"
12
#include "mozilla/dom/NotificationBinding.h"
13
#include "mozilla/dom/WorkerHolder.h"
14
15
#include "nsIObserver.h"
16
#include "nsISupports.h"
17
18
#include "nsCycleCollectionParticipant.h"
19
#include "nsHashKeys.h"
20
#include "nsTHashtable.h"
21
#include "nsWeakReference.h"
22
23
#define NOTIFICATIONTELEMETRYSERVICE_CONTRACTID \
24
0
  "@mozilla.org/notificationTelemetryService;1"
25
26
class nsIPrincipal;
27
class nsIVariant;
28
29
namespace mozilla {
30
namespace dom {
31
32
class NotificationRef;
33
class WorkerNotificationObserver;
34
class Promise;
35
class WorkerPrivate;
36
37
class Notification;
38
class NotificationWorkerHolder final : public WorkerHolder
39
{
40
  // Since the feature is strongly held by a Notification, it is ok to hold
41
  // a raw pointer here.
42
  Notification* mNotification;
43
44
public:
45
  explicit NotificationWorkerHolder(Notification* aNotification);
46
47
  bool
48
  Notify(WorkerStatus aStatus) override;
49
};
50
51
// Records telemetry probes at application startup, when a notification is
52
// shown, and when the notification permission is revoked for a site.
53
class NotificationTelemetryService final : public nsIObserver
54
{
55
public:
56
  NS_DECL_ISUPPORTS
57
  NS_DECL_NSIOBSERVER
58
59
  NotificationTelemetryService();
60
61
  static already_AddRefed<NotificationTelemetryService> GetInstance();
62
63
  nsresult Init();
64
  void RecordDNDSupported();
65
  void RecordPermissions();
66
67
private:
68
  virtual ~NotificationTelemetryService();
69
70
  bool GetNotificationPermission(nsISupports* aSupports,
71
                                 uint32_t* aCapability);
72
73
  bool mDNDRecorded;
74
};
75
76
/*
77
 * Notifications on workers introduce some lifetime issues. The property we
78
 * are trying to satisfy is:
79
 *   Whenever a task is dispatched to the main thread to operate on
80
 *   a Notification, the Notification should be addrefed on the worker thread
81
 *   and a feature should be added to observe the worker lifetime. This main
82
 *   thread owner should ensure it properly releases the reference to the
83
 *   Notification, additionally removing the feature if necessary.
84
 *
85
 * To enforce the correct addref and release, along with managing the feature,
86
 * we introduce a NotificationRef. Only one object may ever own
87
 * a NotificationRef, so UniquePtr<> is used throughout.  The NotificationRef
88
 * constructor calls AddRefObject(). When it is destroyed (on any thread) it
89
 * releases the Notification on the correct thread.
90
 *
91
 * Code should only access the underlying Notification object when it can
92
 * guarantee that it retains ownership of the NotificationRef while doing so.
93
 *
94
 * The one kink in this mechanism is that the worker feature may be Notify()ed
95
 * if the worker stops running script, even if the Notification's corresponding
96
 * UI is still visible to the user. We handle this case with the following
97
 * steps:
98
 *   a) Close the notification. This is done by blocking the worker on the main
99
 *   thread. This ensures that there are no main thread holders when the worker
100
 *   resumes. This also deals with the case where Notify() runs on the worker
101
 *   before the observer has been created on the main thread. Even in such
102
 *   a situation, the CloseNotificationRunnable() will only run after the
103
 *   Show task that was previously queued. Since the show task is only queued
104
 *   once when the Notification is created, we can be sure that no new tasks
105
 *   will follow the Notify().
106
 *
107
 *   b) Ask the observer to let go of its NotificationRef's underlying
108
 *   Notification without proper cleanup since the feature will handle the
109
 *   release. This is only OK because every notification has only one
110
 *   associated observer. The NotificationRef itself is still owned by the
111
 *   observer and deleted by the UniquePtr, but it doesn't do anything since
112
 *   the underlying Notification is null.
113
 *
114
 * To unify code-paths, we use the same NotificationRef in the main
115
 * thread implementation too.
116
 *
117
 * Note that the Notification's JS wrapper does it's standard
118
 * AddRef()/Release() and is not affected by any of this.
119
 *
120
 * Since the worker event queue can have runnables that will dispatch events on
121
 * the Notification, the NotificationRef destructor will first try to release
122
 * the Notification by dispatching a normal runnable to the worker so that it is
123
 * queued after any event runnables. If that dispatch fails, it means the worker
124
 * is no longer running and queued WorkerRunnables will be canceled, so we
125
 * dispatch a control runnable instead.
126
 *
127
 */
128
class Notification : public DOMEventTargetHelper
129
                   , public nsIObserver
130
                   , public nsSupportsWeakReference
131
{
132
  friend class CloseNotificationRunnable;
133
  friend class NotificationTask;
134
  friend class NotificationPermissionRequest;
135
  friend class MainThreadNotificationObserver;
136
  friend class NotificationStorageCallback;
137
  friend class ServiceWorkerNotificationObserver;
138
  friend class WorkerGetRunnable;
139
  friend class WorkerNotificationObserver;
140
  friend class NotificationTelemetryService;
141
142
public:
143
  IMPL_EVENT_HANDLER(click)
144
  IMPL_EVENT_HANDLER(show)
145
  IMPL_EVENT_HANDLER(error)
146
  IMPL_EVENT_HANDLER(close)
147
148
  NS_DECL_ISUPPORTS_INHERITED
149
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(Notification, DOMEventTargetHelper)
150
  NS_DECL_NSIOBSERVER
151
152
  static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
153
  // Returns if Notification.get() is allowed for the current global.
154
  static bool IsGetEnabled(JSContext* aCx, JSObject* aObj);
155
156
  static already_AddRefed<Notification> Constructor(const GlobalObject& aGlobal,
157
                                                    const nsAString& aTitle,
158
                                                    const NotificationOptions& aOption,
159
                                                    ErrorResult& aRv);
160
161
  /**
162
   * Used when dispatching the ServiceWorkerEvent.
163
   *
164
   * Does not initialize the Notification's behavior.
165
   * This is because:
166
   * 1) The Notification is not shown to the user and so the behavior
167
   *    parameters don't matter.
168
   * 2) The default binding requires main thread for parsing the JSON from the
169
   *    string behavior.
170
   */
171
  static already_AddRefed<Notification>
172
  ConstructFromFields(
173
    nsIGlobalObject* aGlobal,
174
    const nsAString& aID,
175
    const nsAString& aTitle,
176
    const nsAString& aDir,
177
    const nsAString& aLang,
178
    const nsAString& aBody,
179
    const nsAString& aTag,
180
    const nsAString& aIcon,
181
    const nsAString& aData,
182
    const nsAString& aServiceWorkerRegistrationScope,
183
    ErrorResult& aRv);
184
185
0
  void GetID(nsAString& aRetval) {
186
0
    aRetval = mID;
187
0
  }
188
189
  void GetTitle(nsAString& aRetval)
190
0
  {
191
0
    aRetval = mTitle;
192
0
  }
193
194
  NotificationDirection Dir()
195
0
  {
196
0
    return mDir;
197
0
  }
198
199
  void GetLang(nsAString& aRetval)
200
0
  {
201
0
    aRetval = mLang;
202
0
  }
203
204
  void GetBody(nsAString& aRetval)
205
0
  {
206
0
    aRetval = mBody;
207
0
  }
208
209
  void GetTag(nsAString& aRetval)
210
0
  {
211
0
    aRetval = mTag;
212
0
  }
213
214
  void GetIcon(nsAString& aRetval)
215
0
  {
216
0
    aRetval = mIconUrl;
217
0
  }
218
219
  void SetStoredState(bool val)
220
0
  {
221
0
    mIsStored = val;
222
0
  }
223
224
  bool IsStored()
225
0
  {
226
0
    return mIsStored;
227
0
  }
228
229
  static bool RequestPermissionEnabledForScope(JSContext* aCx, JSObject* /* unused */);
230
231
  static already_AddRefed<Promise>
232
  RequestPermission(const GlobalObject& aGlobal,
233
                    const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
234
                    ErrorResult& aRv);
235
236
  static NotificationPermission GetPermission(const GlobalObject& aGlobal,
237
                                              ErrorResult& aRv);
238
239
  static already_AddRefed<Promise>
240
  Get(nsPIDOMWindowInner* aWindow,
241
      const GetNotificationOptions& aFilter,
242
      const nsAString& aScope,
243
      ErrorResult& aRv);
244
245
  static already_AddRefed<Promise> Get(const GlobalObject& aGlobal,
246
                                       const GetNotificationOptions& aFilter,
247
                                       ErrorResult& aRv);
248
249
  static already_AddRefed<Promise> WorkerGet(WorkerPrivate* aWorkerPrivate,
250
                                             const GetNotificationOptions& aFilter,
251
                                             const nsAString& aScope,
252
                                             ErrorResult& aRv);
253
254
  // Notification implementation of
255
  // ServiceWorkerRegistration.showNotification.
256
  //
257
  //
258
  // Note that aCx may not be in the compartment of aGlobal, but aOptions will
259
  // have its JS things in the compartment of aCx.
260
  static already_AddRefed<Promise>
261
  ShowPersistentNotification(JSContext* aCx,
262
                             nsIGlobalObject* aGlobal,
263
                             const nsAString& aScope,
264
                             const nsAString& aTitle,
265
                             const NotificationOptions& aOptions,
266
                             ErrorResult& aRv);
267
268
  void Close();
269
270
  nsPIDOMWindowInner* GetParentObject()
271
0
  {
272
0
    return GetOwner();
273
0
  }
274
275
  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
276
277
  bool RequireInteraction() const;
278
279
  void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
280
281
  void InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData, ErrorResult& aRv);
282
283
  void InitFromBase64(const nsAString& aData, ErrorResult& aRv);
284
285
  void AssertIsOnTargetThread() const
286
0
  {
287
0
    MOZ_ASSERT(IsTargetThread());
288
0
  }
289
290
  // Initialized on the worker thread, never unset, and always used in
291
  // a read-only capacity. Used on any thread.
292
  WorkerPrivate* mWorkerPrivate;
293
294
  // Main thread only.
295
  WorkerNotificationObserver* mObserver;
296
297
  // The NotificationTask calls ShowInternal()/CloseInternal() on the
298
  // Notification. At this point the task has ownership of the Notification. It
299
  // passes this on to the Notification itself via mTempRef so that
300
  // ShowInternal()/CloseInternal() may pass it along appropriately (or release
301
  // it).
302
  //
303
  // Main thread only.
304
  UniquePtr<NotificationRef> mTempRef;
305
306
  // Returns true if addref succeeded.
307
  bool AddRefObject();
308
  void ReleaseObject();
309
310
  static NotificationPermission GetPermission(nsIGlobalObject* aGlobal,
311
                                              ErrorResult& aRv);
312
313
  static NotificationPermission GetPermissionInternal(nsIPrincipal* aPrincipal,
314
                                                      ErrorResult& rv);
315
316
  static NotificationPermission TestPermission(nsIPrincipal* aPrincipal);
317
318
  bool DispatchClickEvent();
319
  bool DispatchNotificationClickEvent();
320
321
  static nsresult RemovePermission(nsIPrincipal* aPrincipal);
322
  static nsresult OpenSettings(nsIPrincipal* aPrincipal);
323
324
  nsresult DispatchToMainThread(already_AddRefed<nsIRunnable>&& aRunnable);
325
protected:
326
  Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
327
               const nsAString& aTitle, const nsAString& aBody,
328
               NotificationDirection aDir, const nsAString& aLang,
329
               const nsAString& aTag, const nsAString& aIconUrl,
330
               bool aRequireNotification,
331
               const NotificationBehavior& aBehavior);
332
333
  static already_AddRefed<Notification> CreateInternal(nsIGlobalObject* aGlobal,
334
                                                       const nsAString& aID,
335
                                                       const nsAString& aTitle,
336
                                                       const NotificationOptions& aOptions);
337
338
  nsresult Init();
339
  bool IsInPrivateBrowsing();
340
  void ShowInternal();
341
  void CloseInternal();
342
343
  static NotificationPermission GetPermissionInternal(nsISupports* aGlobal,
344
                                                      ErrorResult& rv);
345
346
  static const nsString DirectionToString(NotificationDirection aDirection)
347
0
  {
348
0
    switch (aDirection) {
349
0
    case NotificationDirection::Ltr:
350
0
      return NS_LITERAL_STRING("ltr");
351
0
    case NotificationDirection::Rtl:
352
0
      return NS_LITERAL_STRING("rtl");
353
0
    default:
354
0
      return NS_LITERAL_STRING("auto");
355
0
    }
356
0
  }
357
358
  static NotificationDirection StringToDirection(const nsAString& aDirection)
359
0
  {
360
0
    if (aDirection.EqualsLiteral("ltr")) {
361
0
      return NotificationDirection::Ltr;
362
0
    }
363
0
    if (aDirection.EqualsLiteral("rtl")) {
364
0
      return NotificationDirection::Rtl;
365
0
    }
366
0
    return NotificationDirection::Auto;
367
0
  }
368
369
  static nsresult GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin);
370
371
  void GetAlertName(nsAString& aRetval)
372
0
  {
373
0
    AssertIsOnMainThread();
374
0
    if (mAlertName.IsEmpty()) {
375
0
      SetAlertName();
376
0
    }
377
0
    aRetval = mAlertName;
378
0
  }
379
380
  void GetScope(nsAString& aScope)
381
0
  {
382
0
    aScope = mScope;
383
0
  }
384
385
  void
386
  SetScope(const nsAString& aScope)
387
0
  {
388
0
    MOZ_ASSERT(mScope.IsEmpty());
389
0
    mScope = aScope;
390
0
  }
391
392
  const nsString mID;
393
  const nsString mTitle;
394
  const nsString mBody;
395
  const NotificationDirection mDir;
396
  const nsString mLang;
397
  const nsString mTag;
398
  const nsString mIconUrl;
399
  const bool mRequireInteraction;
400
  nsString mDataAsBase64;
401
  const NotificationBehavior mBehavior;
402
403
  // It's null until GetData is first called
404
  JS::Heap<JS::Value> mData;
405
406
  nsString mAlertName;
407
  nsString mScope;
408
409
  // Main thread only.
410
  bool mIsClosed;
411
412
  // We need to make a distinction between the notification being closed i.e.
413
  // removed from any pending or active lists, and the notification being
414
  // removed from the database. NotificationDB might fail when trying to remove
415
  // the notification.
416
  bool mIsStored;
417
418
  static uint32_t sCount;
419
420
private:
421
  virtual ~Notification();
422
423
  // Creates a Notification and shows it. Returns a reference to the
424
  // Notification if result is NS_OK. The lifetime of this Notification is tied
425
  // to an underlying NotificationRef. Do not hold a non-stack raw pointer to
426
  // it. Be careful about thread safety if acquiring a strong reference.
427
  //
428
  // Note that aCx may not be in the compartment of aGlobal, but aOptions will
429
  // have its JS things in the compartment of aCx.
430
  static already_AddRefed<Notification>
431
  CreateAndShow(JSContext* aCx,
432
                nsIGlobalObject* aGlobal,
433
                const nsAString& aTitle,
434
                const NotificationOptions& aOptions,
435
                const nsAString& aScope,
436
                ErrorResult& aRv);
437
438
  nsIPrincipal* GetPrincipal();
439
440
  nsresult PersistNotification();
441
  void UnpersistNotification();
442
443
  void
444
  SetAlertName();
445
446
  bool IsTargetThread() const
447
0
  {
448
0
    return NS_IsMainThread() == !mWorkerPrivate;
449
0
  }
450
451
  bool RegisterWorkerHolder();
452
  void UnregisterWorkerHolder();
453
454
  nsresult ResolveIconAndSoundURL(nsString&, nsString&);
455
456
  // Only used for Notifications on Workers, worker thread only.
457
  UniquePtr<NotificationWorkerHolder> mWorkerHolder;
458
  // Target thread only.
459
  uint32_t mTaskCount;
460
};
461
462
} // namespace dom
463
} // namespace mozilla
464
465
#endif // mozilla_dom_notification_h__
466