1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_a11y_NotificationController_h_
7 #define mozilla_a11y_NotificationController_h_
9 #include "EventQueue.h"
11 #include "nsClassHashtable.h"
12 #include "nsCycleCollectionParticipant.h"
14 #include "nsRefreshObservers.h"
15 #include "nsTHashSet.h"
32 * Notification interface.
36 NS_INLINE_DECL_REFCOUNTING(mozilla::a11y::Notification
)
39 * Process notification.
41 virtual void Process() = 0;
47 * Protected destructor, to discourage deletion outside of Release():
49 virtual ~Notification() {}
52 Notification(const Notification
&);
53 Notification
& operator=(const Notification
&);
57 * Template class for generic notification.
59 * @note Instance is kept as a weak ref, the caller must guarantee it exists
60 * longer than the document accessible owning the notification controller
61 * that this notification is processed by.
63 template <class Class
, class... Args
>
64 class TNotification
: public Notification
{
66 typedef void (Class::*Callback
)(Args
*...);
68 TNotification(Class
* aInstance
, Callback aCallback
, Args
*... aArgs
)
69 : mInstance(aInstance
), mCallback(aCallback
), mArgs(aArgs
...) {}
70 virtual ~TNotification() { mInstance
= nullptr; }
72 virtual void Process() override
{
73 ProcessHelper(std::index_sequence_for
<Args
...>{});
77 TNotification(const TNotification
&);
78 TNotification
& operator=(const TNotification
&);
80 template <size_t... Indices
>
81 void ProcessHelper(std::index_sequence
<Indices
...>) {
82 (mInstance
->*mCallback
)(std::get
<Indices
>(mArgs
)...);
87 std::tuple
<RefPtr
<Args
>...> mArgs
;
91 * Used to process notifications from core for the document accessible.
93 class NotificationController final
: public EventQueue
,
94 public nsARefreshObserver
{
96 NotificationController(DocAccessible
* aDocument
, PresShell
* aPresShell
);
98 NS_IMETHOD_(MozExternalRefCountType
) AddRef(void) override
;
99 NS_IMETHOD_(MozExternalRefCountType
) Release(void) override
;
101 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController
)
104 * Shutdown the notification controller.
109 * Add an accessible event into the queue to process it later.
111 void QueueEvent(AccEvent
* aEvent
) {
112 if (PushEvent(aEvent
)) {
113 ScheduleProcessing();
118 * Queue a mutation event to emit if not coalesced away. Returns true if the
119 * event was queued and has not yet been coalesced.
121 bool QueueMutationEvent(AccTreeMutationEvent
* aEvent
);
124 * Coalesce all queued mutation events.
126 void CoalesceMutationEvents();
129 * Schedule binding the child document to the tree of this document.
131 void ScheduleChildDocBinding(DocAccessible
* aDocument
);
134 * Schedule the accessible tree update because of rendered text changes.
136 inline void ScheduleTextUpdate(nsIContent
* aTextNode
) {
137 // Make sure we are not called with a node that is not in the DOM tree or
139 MOZ_ASSERT(aTextNode
->GetParentNode(), "A text node is not in DOM");
140 MOZ_ASSERT(aTextNode
->GetPrimaryFrame(),
141 "A text node doesn't have a frame");
142 MOZ_ASSERT(aTextNode
->GetPrimaryFrame()->StyleVisibility()->IsVisible(),
143 "A text node is not visible");
145 mTextArray
.AppendElement(aTextNode
);
147 ScheduleProcessing();
151 * Pend accessible tree update for content insertion.
153 void ScheduleContentInsertion(LocalAccessible
* aContainer
,
154 nsTArray
<nsCOMPtr
<nsIContent
>>& aInsertions
);
157 * Pend an accessible subtree relocation.
159 void ScheduleRelocation(LocalAccessible
* aOwner
) {
160 if (!mRelocations
.Contains(aOwner
)) {
161 // XXX(Bug 1631371) Check if this should use a fallible operation as it
162 // pretended earlier, or change the return type to void.
163 mRelocations
.AppendElement(aOwner
);
164 ScheduleProcessing();
169 * Start to observe refresh to make notifications and events processing after
172 void ScheduleProcessing();
175 * Process the generic notification synchronously if there are no pending
176 * layout changes and no notifications are pending or being processed right
177 * now. Otherwise, queue it up to process asynchronously.
179 * @note The caller must guarantee that the given instance still exists when
180 * the notification is processed.
182 template <class Class
, class... Args
>
183 inline void HandleNotification(
185 typename TNotification
<Class
, Args
...>::Callback aMethod
,
187 if (!IsUpdatePending()) {
189 if (mozilla::a11y::logging::IsEnabled(
190 mozilla::a11y::logging::eNotifications
)) {
191 mozilla::a11y::logging::Text("sync notification processing");
194 (aInstance
->*aMethod
)(aArgs
...);
198 RefPtr
<Notification
> notification
=
199 new TNotification
<Class
, Args
...>(aInstance
, aMethod
, aArgs
...);
201 // XXX(Bug 1631371) Check if this should use a fallible operation as it
202 // pretended earlier.
203 mNotifications
.AppendElement(notification
);
204 ScheduleProcessing();
209 * Schedule the generic notification to process asynchronously.
211 * @note The caller must guarantee that the given instance still exists when
212 * the notification is processed.
214 template <class Class
>
215 inline void ScheduleNotification(
216 Class
* aInstance
, typename TNotification
<Class
>::Callback aMethod
) {
217 RefPtr
<Notification
> notification
=
218 new TNotification
<Class
>(aInstance
, aMethod
);
220 // XXX(Bug 1631371) Check if this should use a fallible operation as it
221 // pretended earlier.
222 mNotifications
.AppendElement(notification
);
223 ScheduleProcessing();
227 template <class Class
, class Arg
>
228 inline void ScheduleNotification(
229 Class
* aInstance
, typename TNotification
<Class
, Arg
>::Callback aMethod
,
231 RefPtr
<Notification
> notification
=
232 new TNotification
<Class
, Arg
>(aInstance
, aMethod
, aArg
);
234 // XXX(Bug 1631371) Check if this should use a fallible operation as it
235 // pretended earlier.
236 mNotifications
.AppendElement(notification
);
237 ScheduleProcessing();
242 bool IsUpdating() const {
243 return mObservingState
== eRefreshProcessingForUpdate
;
248 virtual ~NotificationController();
250 nsCycleCollectingAutoRefCnt mRefCnt
;
254 * Return true if the accessible tree state update is pending.
256 bool IsUpdatePending();
259 * Return true if we should wait for processing from the parent before we can
260 * process our own queue.
262 bool WaitingForParent();
265 NotificationController(const NotificationController
&);
266 NotificationController
& operator=(const NotificationController
&);
268 // nsARefreshObserver
269 virtual void WillRefresh(mozilla::TimeStamp aTime
) override
;
273 * Remove a specific hide event if it should not be propagated.
275 void CoalesceHideEvent(AccHideEvent
* aHideEvent
);
278 * get rid of a mutation event that is no longer necessary.
280 void DropMutationEvent(AccTreeMutationEvent
* aEvent
);
283 * Fire all necessary mutation events.
285 void ProcessMutationEvents();
288 * Indicates whether we're waiting on an event queue processing from our
289 * notification controller to flush events.
291 enum eObservingState
{
292 eNotObservingRefresh
,
295 eRefreshProcessingForUpdate
297 eObservingState mObservingState
;
300 * The presshell of the document accessible.
302 PresShell
* mPresShell
;
305 * Child documents that needs to be bound to the tree.
307 nsTArray
<RefPtr
<DocAccessible
>> mHangingChildDocuments
;
310 * Pending accessible tree update notifications for content insertions.
312 nsClassHashtable
<nsRefPtrHashKey
<LocalAccessible
>,
313 nsTArray
<nsCOMPtr
<nsIContent
>>>
317 class nsCOMPtrHashKey
: public PLDHashEntryHdr
{
320 typedef const T
* KeyTypePointer
;
322 explicit nsCOMPtrHashKey(const T
* aKey
) : mKey(const_cast<T
*>(aKey
)) {}
323 nsCOMPtrHashKey(nsCOMPtrHashKey
<T
>&& aOther
)
324 : PLDHashEntryHdr(std::move(aOther
)), mKey(std::move(aOther
.mKey
)) {}
325 ~nsCOMPtrHashKey() {}
327 KeyType
GetKey() const { return mKey
; }
328 bool KeyEquals(KeyTypePointer aKey
) const { return aKey
== mKey
; }
330 static KeyTypePointer
KeyToPointer(KeyType aKey
) { return aKey
; }
331 static PLDHashNumber
HashKey(KeyTypePointer aKey
) {
332 return NS_PTR_TO_INT32(aKey
) >> 2;
335 enum { ALLOW_MEMMOVE
= true };
342 * Pending accessible tree update notifications for rendered text changes.
343 * When there are a lot of nearby text insertions (e.g. during a reflow), it
344 * is much more performant to process them in order because we then benefit
345 * from the layout line cursor. Therefore, we use an array here.
347 nsTArray
<nsCOMPtr
<nsIContent
>> mTextArray
;
350 * Other notifications like DOM events. Don't make this an AutoTArray; we
351 * use SwapElements() on it.
353 nsTArray
<RefPtr
<Notification
>> mNotifications
;
356 * Holds all scheduled relocations.
358 nsTArray
<RefPtr
<LocalAccessible
>> mRelocations
;
361 * A list of all mutation events we may want to emit. Ordered from the first
362 * event that should be emitted to the last one to emit.
364 RefPtr
<AccTreeMutationEvent
> mFirstMutationEvent
;
365 RefPtr
<AccTreeMutationEvent
> mLastMutationEvent
;
368 * A class to map an accessible and event type to an event.
378 void PutEvent(AccTreeMutationEvent
* aEvent
);
379 AccTreeMutationEvent
* GetEvent(LocalAccessible
* aTarget
, EventType aType
);
380 void RemoveEvent(AccTreeMutationEvent
* aEvent
);
381 void Clear() { mTable
.Clear(); }
384 EventType
GetEventType(AccTreeMutationEvent
* aEvent
);
386 nsRefPtrHashtable
<nsUint64HashKey
, AccTreeMutationEvent
> mTable
;
389 EventMap mMutationMap
;
390 uint32_t mEventGeneration
;
394 } // namespace mozilla
396 #endif // mozilla_a11y_NotificationController_h_