Bug 1664775 [wpt PR 25515] - Fieldset NG: Fix hit-testing for the rendered legend...
[gecko.git] / dom / notification / Notification.h
blob9981e551dacf6c89ac8f89e78081acb3e375de43
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"
14 #include "nsIObserver.h"
15 #include "nsISupports.h"
17 #include "nsCycleCollectionParticipant.h"
18 #include "nsHashKeys.h"
19 #include "nsTHashtable.h"
20 #include "nsWeakReference.h"
22 class nsIPrincipal;
23 class nsIVariant;
25 namespace mozilla {
26 namespace dom {
28 class NotificationRef;
29 class WorkerNotificationObserver;
30 class Promise;
31 class StrongWorkerRef;
32 class WorkerPrivate;
35 * Notifications on workers introduce some lifetime issues. The property we
36 * are trying to satisfy is:
37 * Whenever a task is dispatched to the main thread to operate on
38 * a Notification, the Notification should be addrefed on the worker thread
39 * and a feature should be added to observe the worker lifetime. This main
40 * thread owner should ensure it properly releases the reference to the
41 * Notification, additionally removing the feature if necessary.
43 * To enforce the correct addref and release, along with managing the feature,
44 * we introduce a NotificationRef. Only one object may ever own
45 * a NotificationRef, so UniquePtr<> is used throughout. The NotificationRef
46 * constructor calls AddRefObject(). When it is destroyed (on any thread) it
47 * releases the Notification on the correct thread.
49 * Code should only access the underlying Notification object when it can
50 * guarantee that it retains ownership of the NotificationRef while doing so.
52 * The one kink in this mechanism is that the worker feature may be Notify()ed
53 * if the worker stops running script, even if the Notification's corresponding
54 * UI is still visible to the user. We handle this case with the following
55 * steps:
56 * a) Close the notification. This is done by blocking the worker on the main
57 * thread. This ensures that there are no main thread holders when the worker
58 * resumes. This also deals with the case where Notify() runs on the worker
59 * before the observer has been created on the main thread. Even in such
60 * a situation, the CloseNotificationRunnable() will only run after the
61 * Show task that was previously queued. Since the show task is only queued
62 * once when the Notification is created, we can be sure that no new tasks
63 * will follow the Notify().
65 * b) Ask the observer to let go of its NotificationRef's underlying
66 * Notification without proper cleanup since the feature will handle the
67 * release. This is only OK because every notification has only one
68 * associated observer. The NotificationRef itself is still owned by the
69 * observer and deleted by the UniquePtr, but it doesn't do anything since
70 * the underlying Notification is null.
72 * To unify code-paths, we use the same NotificationRef in the main
73 * thread implementation too.
75 * Note that the Notification's JS wrapper does it's standard
76 * AddRef()/Release() and is not affected by any of this.
78 * Since the worker event queue can have runnables that will dispatch events on
79 * the Notification, the NotificationRef destructor will first try to release
80 * the Notification by dispatching a normal runnable to the worker so that it is
81 * queued after any event runnables. If that dispatch fails, it means the worker
82 * is no longer running and queued WorkerRunnables will be canceled, so we
83 * dispatch a control runnable instead.
86 class Notification : public DOMEventTargetHelper,
87 public nsIObserver,
88 public nsSupportsWeakReference {
89 friend class CloseNotificationRunnable;
90 friend class NotificationTask;
91 friend class NotificationPermissionRequest;
92 friend class MainThreadNotificationObserver;
93 friend class NotificationStorageCallback;
94 friend class ServiceWorkerNotificationObserver;
95 friend class WorkerGetRunnable;
96 friend class WorkerNotificationObserver;
98 public:
99 IMPL_EVENT_HANDLER(click)
100 IMPL_EVENT_HANDLER(show)
101 IMPL_EVENT_HANDLER(error)
102 IMPL_EVENT_HANDLER(close)
104 NS_DECL_ISUPPORTS_INHERITED
105 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(Notification,
106 DOMEventTargetHelper)
107 NS_DECL_NSIOBSERVER
109 static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
110 // Returns if Notification.get() is allowed for the current global.
111 static bool IsGetEnabled(JSContext* aCx, JSObject* aObj);
113 static already_AddRefed<Notification> Constructor(
114 const GlobalObject& aGlobal, const nsAString& aTitle,
115 const NotificationOptions& aOption, ErrorResult& aRv);
118 * Used when dispatching the ServiceWorkerEvent.
120 * Does not initialize the Notification's behavior.
121 * This is because:
122 * 1) The Notification is not shown to the user and so the behavior
123 * parameters don't matter.
124 * 2) The default binding requires main thread for parsing the JSON from the
125 * string behavior.
127 static already_AddRefed<Notification> ConstructFromFields(
128 nsIGlobalObject* aGlobal, const nsAString& aID, const nsAString& aTitle,
129 const nsAString& aDir, const nsAString& aLang, const nsAString& aBody,
130 const nsAString& aTag, const nsAString& aIcon, const nsAString& aData,
131 const nsAString& aServiceWorkerRegistrationScope, ErrorResult& aRv);
133 void GetID(nsAString& aRetval) { aRetval = mID; }
135 void GetTitle(nsAString& aRetval) { aRetval = mTitle; }
137 NotificationDirection Dir() { return mDir; }
139 void GetLang(nsAString& aRetval) { aRetval = mLang; }
141 void GetBody(nsAString& aRetval) { aRetval = mBody; }
143 void GetTag(nsAString& aRetval) { aRetval = mTag; }
145 void GetIcon(nsAString& aRetval) { aRetval = mIconUrl; }
147 void SetStoredState(bool val) { mIsStored = val; }
149 bool IsStored() { return mIsStored; }
151 static bool RequestPermissionEnabledForScope(JSContext* aCx,
152 JSObject* /* unused */);
154 static already_AddRefed<Promise> RequestPermission(
155 const GlobalObject& aGlobal,
156 const Optional<OwningNonNull<NotificationPermissionCallback> >& aCallback,
157 ErrorResult& aRv);
159 static NotificationPermission GetPermission(const GlobalObject& aGlobal,
160 ErrorResult& aRv);
162 static already_AddRefed<Promise> Get(nsPIDOMWindowInner* aWindow,
163 const GetNotificationOptions& aFilter,
164 const nsAString& aScope,
165 ErrorResult& aRv);
167 static already_AddRefed<Promise> Get(const GlobalObject& aGlobal,
168 const GetNotificationOptions& aFilter,
169 ErrorResult& aRv);
171 static already_AddRefed<Promise> WorkerGet(
172 WorkerPrivate* aWorkerPrivate, const GetNotificationOptions& aFilter,
173 const nsAString& aScope, ErrorResult& aRv);
175 // Notification implementation of
176 // ServiceWorkerRegistration.showNotification.
179 // Note that aCx may not be in the compartment of aGlobal, but aOptions will
180 // have its JS things in the compartment of aCx.
181 static already_AddRefed<Promise> ShowPersistentNotification(
182 JSContext* aCx, nsIGlobalObject* aGlobal, const nsAString& aScope,
183 const nsAString& aTitle, const NotificationOptions& aOptions,
184 const ServiceWorkerRegistrationDescriptor& aDescriptor, ErrorResult& aRv);
186 void Close();
188 nsPIDOMWindowInner* GetParentObject() { return GetOwner(); }
190 virtual JSObject* WrapObject(JSContext* aCx,
191 JS::Handle<JSObject*> aGivenProto) override;
193 bool RequireInteraction() const;
195 void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval);
197 void InitFromJSVal(JSContext* aCx, JS::Handle<JS::Value> aData,
198 ErrorResult& aRv);
200 void InitFromBase64(const nsAString& aData, ErrorResult& aRv);
202 void AssertIsOnTargetThread() const { MOZ_ASSERT(IsTargetThread()); }
204 // Initialized on the worker thread, never unset, and always used in
205 // a read-only capacity. Used on any thread.
206 WorkerPrivate* mWorkerPrivate;
208 // Main thread only.
209 WorkerNotificationObserver* mObserver;
211 // The NotificationTask calls ShowInternal()/CloseInternal() on the
212 // Notification. At this point the task has ownership of the Notification. It
213 // passes this on to the Notification itself via mTempRef so that
214 // ShowInternal()/CloseInternal() may pass it along appropriately (or release
215 // it).
217 // Main thread only.
218 UniquePtr<NotificationRef> mTempRef;
220 // Returns true if addref succeeded.
221 bool AddRefObject();
222 void ReleaseObject();
224 static NotificationPermission GetPermission(nsIGlobalObject* aGlobal,
225 ErrorResult& aRv);
227 static NotificationPermission GetPermissionInternal(nsIPrincipal* aPrincipal,
228 ErrorResult& rv);
230 static NotificationPermission TestPermission(nsIPrincipal* aPrincipal);
232 bool DispatchClickEvent();
234 static nsresult RemovePermission(nsIPrincipal* aPrincipal);
235 static nsresult OpenSettings(nsIPrincipal* aPrincipal);
237 nsresult DispatchToMainThread(already_AddRefed<nsIRunnable>&& aRunnable);
239 protected:
240 Notification(nsIGlobalObject* aGlobal, const nsAString& aID,
241 const nsAString& aTitle, const nsAString& aBody,
242 NotificationDirection aDir, const nsAString& aLang,
243 const nsAString& aTag, const nsAString& aIconUrl,
244 bool aRequireNotification,
245 const NotificationBehavior& aBehavior);
247 static already_AddRefed<Notification> CreateInternal(
248 nsIGlobalObject* aGlobal, const nsAString& aID, const nsAString& aTitle,
249 const NotificationOptions& aOptions);
251 nsresult Init();
252 bool IsInPrivateBrowsing();
253 void ShowInternal();
254 void CloseInternal();
256 static NotificationPermission GetPermissionInternal(nsISupports* aGlobal,
257 ErrorResult& rv);
259 static const nsString DirectionToString(NotificationDirection aDirection) {
260 switch (aDirection) {
261 case NotificationDirection::Ltr:
262 return u"ltr"_ns;
263 case NotificationDirection::Rtl:
264 return u"rtl"_ns;
265 default:
266 return u"auto"_ns;
270 static NotificationDirection StringToDirection(const nsAString& aDirection) {
271 if (aDirection.EqualsLiteral("ltr")) {
272 return NotificationDirection::Ltr;
274 if (aDirection.EqualsLiteral("rtl")) {
275 return NotificationDirection::Rtl;
277 return NotificationDirection::Auto;
280 static nsresult GetOrigin(nsIPrincipal* aPrincipal, nsString& aOrigin);
282 void GetAlertName(nsAString& aRetval) {
283 AssertIsOnMainThread();
284 if (mAlertName.IsEmpty()) {
285 SetAlertName();
287 aRetval = mAlertName;
290 void GetScope(nsAString& aScope) { aScope = mScope; }
292 void SetScope(const nsAString& aScope) {
293 MOZ_ASSERT(mScope.IsEmpty());
294 mScope = aScope;
297 const nsString mID;
298 const nsString mTitle;
299 const nsString mBody;
300 const NotificationDirection mDir;
301 const nsString mLang;
302 const nsString mTag;
303 const nsString mIconUrl;
304 const bool mRequireInteraction;
305 nsString mDataAsBase64;
306 const NotificationBehavior mBehavior;
308 // It's null until GetData is first called
309 JS::Heap<JS::Value> mData;
311 nsString mAlertName;
312 nsString mScope;
314 // Main thread only.
315 bool mIsClosed;
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
320 // the notification.
321 bool mIsStored;
323 static uint32_t sCount;
325 private:
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,
338 ErrorResult& aRv);
340 nsIPrincipal* GetPrincipal();
342 nsresult PersistNotification();
343 void UnpersistNotification();
345 void SetAlertName();
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.
356 uint32_t mTaskCount;
359 } // namespace dom
360 } // namespace mozilla
362 #endif // mozilla_dom_notification_h__