no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / notification / Notification.h
blob4ffa69cf49f5be0675c1f2e3e8d698f88136df36
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"
22 class nsIPrincipal;
23 class nsIVariant;
25 namespace mozilla::dom {
27 class NotificationRef;
28 class WorkerNotificationObserver;
29 class Promise;
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
53 * steps:
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,
85 public nsIObserver,
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;
96 public:
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)
105 NS_DECL_NSIOBSERVER
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.
117 * This is because:
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
121 * string behavior.
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,
153 ErrorResult& aRv);
155 static NotificationPermission GetPermission(const GlobalObject& aGlobal,
156 ErrorResult& aRv);
158 static already_AddRefed<Promise> Get(nsPIDOMWindowInner* aWindow,
159 const GetNotificationOptions& aFilter,
160 const nsAString& aScope,
161 ErrorResult& aRv);
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);
178 void Close();
180 nsPIDOMWindowInner* GetParentObject() { return GetOwner(); }
182 virtual JSObject* WrapObject(JSContext* aCx,
183 JS::Handle<JSObject*> aGivenProto) override;
185 bool RequireInteraction() const;
187 bool Silent() 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,
194 ErrorResult& aRv);
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;
204 // Main thread only.
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
211 // it).
213 // Main thread only.
214 UniquePtr<NotificationRef> mTempRef;
216 // Returns true if addref succeeded.
217 bool AddRefObject();
218 void ReleaseObject();
220 static NotificationPermission GetPermission(nsIGlobalObject* aGlobal,
221 ErrorResult& aRv);
223 static NotificationPermission GetPermissionInternal(nsIPrincipal* aPrincipal,
224 ErrorResult& rv);
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);
235 protected:
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 nsresult Init();
249 bool IsInPrivateBrowsing();
250 void ShowInternal();
251 void CloseInternal(bool aContextClosed = false);
253 static NotificationPermission GetPermissionInternal(
254 nsPIDOMWindowInner* aWindow, ErrorResult& rv);
256 static const nsString DirectionToString(NotificationDirection aDirection) {
257 switch (aDirection) {
258 case NotificationDirection::Ltr:
259 return u"ltr"_ns;
260 case NotificationDirection::Rtl:
261 return u"rtl"_ns;
262 default:
263 return u"auto"_ns;
267 static NotificationDirection StringToDirection(const nsAString& aDirection) {
268 if (aDirection.EqualsLiteral("ltr")) {
269 return NotificationDirection::Ltr;
271 if (aDirection.EqualsLiteral("rtl")) {
272 return NotificationDirection::Rtl;
274 return NotificationDirection::Auto;
277 static nsresult GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin);
279 void GetAlertName(nsAString& aRetval) {
280 AssertIsOnMainThread();
281 if (mAlertName.IsEmpty()) {
282 SetAlertName();
284 aRetval = mAlertName;
287 void GetScope(nsAString& aScope) { aScope = mScope; }
289 void SetScope(const nsAString& aScope) {
290 MOZ_ASSERT(mScope.IsEmpty());
291 mScope = aScope;
294 const nsString mID;
295 const nsString mTitle;
296 const nsString mBody;
297 const NotificationDirection mDir;
298 const nsString mLang;
299 const nsString mTag;
300 const nsString mIconUrl;
301 const bool mRequireInteraction;
302 const bool mSilent;
303 nsTArray<uint32_t> mVibrate;
304 nsString mDataAsBase64;
305 const NotificationBehavior mBehavior;
307 // It's null until GetData is first called
308 JS::Heap<JS::Value> mData;
310 nsString mAlertName;
311 nsString mScope;
313 // Main thread only.
314 bool mIsClosed;
316 // We need to make a distinction between the notification being closed i.e.
317 // removed from any pending or active lists, and the notification being
318 // removed from the database. NotificationDB might fail when trying to remove
319 // the notification.
320 bool mIsStored;
322 static uint32_t sCount;
324 private:
325 virtual ~Notification();
327 // Creates a Notification and shows it. Returns a reference to the
328 // Notification if result is NS_OK. The lifetime of this Notification is tied
329 // to an underlying NotificationRef. Do not hold a non-stack raw pointer to
330 // it. Be careful about thread safety if acquiring a strong reference.
332 // Note that aCx may not be in the compartment of aGlobal, but aOptions will
333 // have its JS things in the compartment of aCx.
334 static already_AddRefed<Notification> CreateAndShow(
335 JSContext* aCx, nsIGlobalObject* aGlobal, const nsAString& aTitle,
336 const NotificationOptions& aOptions, const nsAString& aScope,
337 ErrorResult& aRv);
339 nsIPrincipal* GetPrincipal();
341 nsresult PersistNotification();
342 void UnpersistNotification();
344 void SetAlertName();
346 bool IsTargetThread() const { return NS_IsMainThread() == !mWorkerPrivate; }
348 bool CreateWorkerRef();
350 nsresult ResolveIconAndSoundURL(nsString&, nsString&);
352 // Only used for Notifications on Workers, worker thread only.
353 RefPtr<StrongWorkerRef> mWorkerRef;
354 // Target thread only.
355 uint32_t mTaskCount;
358 } // namespace mozilla::dom
360 #endif // mozilla_dom_notification_h__