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/. */
7 #ifndef mozilla_dom_notification_h__
8 #define mozilla_dom_notification_h__
10 #include "mozilla/DOMEventTargetHelper.h"
11 #include "mozilla/UniquePtr.h"
12 #include "mozilla/dom/NotificationBinding.h"
13 #include "mozilla/dom/WorkerPrivate.h"
14 #include "mozilla/dom/quota/QuotaCommon.h"
16 #include "nsIObserver.h"
17 #include "nsISupports.h"
19 #include "nsCycleCollectionParticipant.h"
20 #include "nsWeakReference.h"
25 namespace mozilla::dom
{
27 class NotificationRef
;
28 class WorkerNotificationObserver
;
30 class StrongWorkerRef
;
33 * Notifications on workers introduce some lifetime issues. The property we
34 * are trying to satisfy is:
35 * Whenever a task is dispatched to the main thread to operate on
36 * a Notification, the Notification should be addrefed on the worker thread
37 * and a feature should be added to observe the worker lifetime. This main
38 * thread owner should ensure it properly releases the reference to the
39 * Notification, additionally removing the feature if necessary.
41 * To enforce the correct addref and release, along with managing the feature,
42 * we introduce a NotificationRef. Only one object may ever own
43 * a NotificationRef, so UniquePtr<> is used throughout. The NotificationRef
44 * constructor calls AddRefObject(). When it is destroyed (on any thread) it
45 * releases the Notification on the correct thread.
47 * Code should only access the underlying Notification object when it can
48 * guarantee that it retains ownership of the NotificationRef while doing so.
50 * The one kink in this mechanism is that the worker feature may be Notify()ed
51 * if the worker stops running script, even if the Notification's corresponding
52 * UI is still visible to the user. We handle this case with the following
54 * a) Close the notification. This is done by blocking the worker on the main
55 * thread. This ensures that there are no main thread holders when the worker
56 * resumes. This also deals with the case where Notify() runs on the worker
57 * before the observer has been created on the main thread. Even in such
58 * a situation, the CloseNotificationRunnable() will only run after the
59 * Show task that was previously queued. Since the show task is only queued
60 * once when the Notification is created, we can be sure that no new tasks
61 * will follow the Notify().
63 * b) Ask the observer to let go of its NotificationRef's underlying
64 * Notification without proper cleanup since the feature will handle the
65 * release. This is only OK because every notification has only one
66 * associated observer. The NotificationRef itself is still owned by the
67 * observer and deleted by the UniquePtr, but it doesn't do anything since
68 * the underlying Notification is null.
70 * To unify code-paths, we use the same NotificationRef in the main
71 * thread implementation too.
73 * Note that the Notification's JS wrapper does it's standard
74 * AddRef()/Release() and is not affected by any of this.
76 * Since the worker event queue can have runnables that will dispatch events on
77 * the Notification, the NotificationRef destructor will first try to release
78 * the Notification by dispatching a normal runnable to the worker so that it is
79 * queued after any event runnables. If that dispatch fails, it means the worker
80 * is no longer running and queued WorkerRunnables will be canceled, so we
81 * dispatch a control runnable instead.
84 class Notification
: public DOMEventTargetHelper
,
86 public nsSupportsWeakReference
{
87 friend class CloseNotificationRunnable
;
88 friend class NotificationTask
;
89 friend class NotificationPermissionRequest
;
90 friend class MainThreadNotificationObserver
;
91 friend class NotificationStorageCallback
;
92 friend class ServiceWorkerNotificationObserver
;
93 friend class WorkerGetRunnable
;
94 friend class WorkerNotificationObserver
;
97 IMPL_EVENT_HANDLER(click
)
98 IMPL_EVENT_HANDLER(show
)
99 IMPL_EVENT_HANDLER(error
)
100 IMPL_EVENT_HANDLER(close
)
102 NS_DECL_ISUPPORTS_INHERITED
103 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(Notification
,
104 DOMEventTargetHelper
)
107 static bool PrefEnabled(JSContext
* aCx
, JSObject
* aObj
);
109 static already_AddRefed
<Notification
> Constructor(
110 const GlobalObject
& aGlobal
, const nsAString
& aTitle
,
111 const NotificationOptions
& aOption
, ErrorResult
& aRv
);
114 * Used when dispatching the ServiceWorkerEvent.
116 * Does not initialize the Notification's behavior.
118 * 1) The Notification is not shown to the user and so the behavior
119 * parameters don't matter.
120 * 2) The default binding requires main thread for parsing the JSON from the
123 static Result
<already_AddRefed
<Notification
>, QMResult
> ConstructFromFields(
124 nsIGlobalObject
* aGlobal
, const nsAString
& aID
, const nsAString
& aTitle
,
125 const nsAString
& aDir
, const nsAString
& aLang
, const nsAString
& aBody
,
126 const nsAString
& aTag
, const nsAString
& aIcon
, const nsAString
& aData
,
127 const nsAString
& aServiceWorkerRegistrationScope
);
129 void GetID(nsAString
& aRetval
) { aRetval
= mID
; }
131 void GetTitle(nsAString
& aRetval
) { aRetval
= mTitle
; }
133 NotificationDirection
Dir() { return mDir
; }
135 void GetLang(nsAString
& aRetval
) { aRetval
= mLang
; }
137 void GetBody(nsAString
& aRetval
) { aRetval
= mBody
; }
139 void GetTag(nsAString
& aRetval
) { aRetval
= mTag
; }
141 void GetIcon(nsAString
& aRetval
) { aRetval
= mIconUrl
; }
143 void SetStoredState(bool val
) { mIsStored
= val
; }
145 bool IsStored() { return mIsStored
; }
147 static bool RequestPermissionEnabledForScope(JSContext
* aCx
,
148 JSObject
* /* unused */);
150 static already_AddRefed
<Promise
> RequestPermission(
151 const GlobalObject
& aGlobal
,
152 const Optional
<OwningNonNull
<NotificationPermissionCallback
> >& aCallback
,
155 static NotificationPermission
GetPermission(const GlobalObject
& aGlobal
,
158 static already_AddRefed
<Promise
> Get(nsPIDOMWindowInner
* aWindow
,
159 const GetNotificationOptions
& aFilter
,
160 const nsAString
& aScope
,
163 static already_AddRefed
<Promise
> WorkerGet(
164 WorkerPrivate
* aWorkerPrivate
, const GetNotificationOptions
& aFilter
,
165 const nsAString
& aScope
, ErrorResult
& aRv
);
167 // Notification implementation of
168 // ServiceWorkerRegistration.showNotification.
171 // Note that aCx may not be in the compartment of aGlobal, but aOptions will
172 // have its JS things in the compartment of aCx.
173 static already_AddRefed
<Promise
> ShowPersistentNotification(
174 JSContext
* aCx
, nsIGlobalObject
* aGlobal
, const nsAString
& aScope
,
175 const nsAString
& aTitle
, const NotificationOptions
& aOptions
,
176 const ServiceWorkerRegistrationDescriptor
& aDescriptor
, ErrorResult
& aRv
);
180 nsPIDOMWindowInner
* GetParentObject() { return GetOwner(); }
182 virtual JSObject
* WrapObject(JSContext
* aCx
,
183 JS::Handle
<JSObject
*> aGivenProto
) override
;
185 bool RequireInteraction() const;
189 void GetVibrate(nsTArray
<uint32_t>& aRetval
) const;
191 void GetData(JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aRetval
);
193 void InitFromJSVal(JSContext
* aCx
, JS::Handle
<JS::Value
> aData
,
196 Result
<Ok
, QMResult
> InitFromBase64(const nsAString
& aData
);
198 void AssertIsOnTargetThread() const { MOZ_ASSERT(IsTargetThread()); }
200 // Initialized on the worker thread, never unset, and always used in
201 // a read-only capacity. Used on any thread.
202 CheckedUnsafePtr
<WorkerPrivate
> mWorkerPrivate
;
205 WorkerNotificationObserver
* mObserver
;
207 // The NotificationTask calls ShowInternal()/CloseInternal() on the
208 // Notification. At this point the task has ownership of the Notification. It
209 // passes this on to the Notification itself via mTempRef so that
210 // ShowInternal()/CloseInternal() may pass it along appropriately (or release
214 UniquePtr
<NotificationRef
> mTempRef
;
216 // Returns true if addref succeeded.
218 void ReleaseObject();
220 static NotificationPermission
GetPermission(nsIGlobalObject
* aGlobal
,
223 static NotificationPermission
GetPermissionInternal(nsIPrincipal
* aPrincipal
,
226 static NotificationPermission
TestPermission(nsIPrincipal
* aPrincipal
);
228 bool DispatchClickEvent();
230 static nsresult
RemovePermission(nsIPrincipal
* aPrincipal
);
231 static nsresult
OpenSettings(nsIPrincipal
* aPrincipal
);
233 nsresult
DispatchToMainThread(already_AddRefed
<nsIRunnable
>&& aRunnable
);
236 Notification(nsIGlobalObject
* aGlobal
, const nsAString
& aID
,
237 const nsAString
& aTitle
, const nsAString
& aBody
,
238 NotificationDirection aDir
, const nsAString
& aLang
,
239 const nsAString
& aTag
, const nsAString
& aIconUrl
,
240 bool aRequireInteraction
, bool aSilent
,
241 nsTArray
<uint32_t>&& aVibrate
,
242 const NotificationBehavior
& aBehavior
);
244 static already_AddRefed
<Notification
> CreateInternal(
245 nsIGlobalObject
* aGlobal
, const nsAString
& aID
, const nsAString
& aTitle
,
246 const NotificationOptions
& aOptions
, ErrorResult
& aRv
);
248 // Triggers CloseInternal for non-persistent notifications if window goes away
249 nsresult
MaybeObserveWindowFrozenOrDestroyed();
250 bool IsInPrivateBrowsing();
252 void CloseInternal(bool aContextClosed
= false);
254 static NotificationPermission
GetPermissionInternal(
255 nsPIDOMWindowInner
* aWindow
, ErrorResult
& rv
);
257 static const nsString
DirectionToString(NotificationDirection aDirection
) {
258 switch (aDirection
) {
259 case NotificationDirection::Ltr
:
261 case NotificationDirection::Rtl
:
268 static NotificationDirection
StringToDirection(const nsAString
& aDirection
) {
269 if (aDirection
.EqualsLiteral("ltr")) {
270 return NotificationDirection::Ltr
;
272 if (aDirection
.EqualsLiteral("rtl")) {
273 return NotificationDirection::Rtl
;
275 return NotificationDirection::Auto
;
278 static nsresult
GetOrigin(nsIPrincipal
* aPrincipal
, nsString
& aOrigin
);
280 void GetAlertName(nsAString
& aRetval
) {
281 AssertIsOnMainThread();
282 if (mAlertName
.IsEmpty()) {
285 aRetval
= mAlertName
;
288 void GetScope(nsAString
& aScope
) { aScope
= mScope
; }
290 void SetScope(const nsAString
& aScope
) {
291 MOZ_ASSERT(mScope
.IsEmpty());
296 const nsString mTitle
;
297 const nsString mBody
;
298 const NotificationDirection mDir
;
299 const nsString mLang
;
301 const nsString mIconUrl
;
302 const bool mRequireInteraction
;
304 nsTArray
<uint32_t> mVibrate
;
305 nsString mDataAsBase64
;
306 const NotificationBehavior mBehavior
;
308 // It's null until GetData is first called
309 JS::Heap
<JS::Value
> mData
;
317 // We need to make a distinction between the notification being closed i.e.
318 // removed from any pending or active lists, and the notification being
319 // removed from the database. NotificationDB might fail when trying to remove
323 static uint32_t sCount
;
326 virtual ~Notification();
328 // Creates a Notification and shows it. Returns a reference to the
329 // Notification if result is NS_OK. The lifetime of this Notification is tied
330 // to an underlying NotificationRef. Do not hold a non-stack raw pointer to
331 // it. Be careful about thread safety if acquiring a strong reference.
333 // Note that aCx may not be in the compartment of aGlobal, but aOptions will
334 // have its JS things in the compartment of aCx.
335 static already_AddRefed
<Notification
> CreateAndShow(
336 JSContext
* aCx
, nsIGlobalObject
* aGlobal
, const nsAString
& aTitle
,
337 const NotificationOptions
& aOptions
, const nsAString
& aScope
,
340 nsIPrincipal
* GetPrincipal();
342 nsresult
PersistNotification();
343 void UnpersistNotification();
347 bool IsTargetThread() const { return NS_IsMainThread() == !mWorkerPrivate
; }
349 bool CreateWorkerRef();
351 nsresult
ResolveIconAndSoundURL(nsString
&, nsString
&);
353 // Only used for Notifications on Workers, worker thread only.
354 RefPtr
<StrongWorkerRef
> mWorkerRef
;
355 // Target thread only.
359 } // namespace mozilla::dom
361 #endif // mozilla_dom_notification_h__