|
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/. */ |
|
5 |
|
6 #ifndef mozilla_a11y_NotificationController_h_ |
|
7 #define mozilla_a11y_NotificationController_h_ |
|
8 |
|
9 #include "EventQueue.h" |
|
10 |
|
11 #include "nsCycleCollectionParticipant.h" |
|
12 #include "nsRefreshDriver.h" |
|
13 |
|
14 #ifdef A11Y_LOG |
|
15 #include "Logging.h" |
|
16 #endif |
|
17 |
|
18 namespace mozilla { |
|
19 namespace a11y { |
|
20 |
|
21 class DocAccessible; |
|
22 |
|
23 /** |
|
24 * Notification interface. |
|
25 */ |
|
26 class Notification |
|
27 { |
|
28 public: |
|
29 NS_INLINE_DECL_REFCOUNTING(Notification) |
|
30 |
|
31 /** |
|
32 * Process notification. |
|
33 */ |
|
34 virtual void Process() = 0; |
|
35 |
|
36 protected: |
|
37 Notification() { } |
|
38 |
|
39 /** |
|
40 * Protected destructor, to discourage deletion outside of Release(): |
|
41 */ |
|
42 virtual ~Notification() { } |
|
43 |
|
44 private: |
|
45 Notification(const Notification&); |
|
46 Notification& operator = (const Notification&); |
|
47 }; |
|
48 |
|
49 |
|
50 /** |
|
51 * Template class for generic notification. |
|
52 * |
|
53 * @note Instance is kept as a weak ref, the caller must guarantee it exists |
|
54 * longer than the document accessible owning the notification controller |
|
55 * that this notification is processed by. |
|
56 */ |
|
57 template<class Class, class Arg> |
|
58 class TNotification : public Notification |
|
59 { |
|
60 public: |
|
61 typedef void (Class::*Callback)(Arg*); |
|
62 |
|
63 TNotification(Class* aInstance, Callback aCallback, Arg* aArg) : |
|
64 mInstance(aInstance), mCallback(aCallback), mArg(aArg) { } |
|
65 virtual ~TNotification() { mInstance = nullptr; } |
|
66 |
|
67 virtual void Process() |
|
68 { |
|
69 (mInstance->*mCallback)(mArg); |
|
70 |
|
71 mInstance = nullptr; |
|
72 mCallback = nullptr; |
|
73 mArg = nullptr; |
|
74 } |
|
75 |
|
76 private: |
|
77 TNotification(const TNotification&); |
|
78 TNotification& operator = (const TNotification&); |
|
79 |
|
80 Class* mInstance; |
|
81 Callback mCallback; |
|
82 nsRefPtr<Arg> mArg; |
|
83 }; |
|
84 |
|
85 /** |
|
86 * Used to process notifications from core for the document accessible. |
|
87 */ |
|
88 class NotificationController : public EventQueue, |
|
89 public nsARefreshObserver |
|
90 { |
|
91 public: |
|
92 NotificationController(DocAccessible* aDocument, nsIPresShell* aPresShell); |
|
93 virtual ~NotificationController(); |
|
94 |
|
95 NS_IMETHOD_(MozExternalRefCountType) AddRef(void); |
|
96 NS_IMETHOD_(MozExternalRefCountType) Release(void); |
|
97 |
|
98 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController) |
|
99 |
|
100 /** |
|
101 * Shutdown the notification controller. |
|
102 */ |
|
103 void Shutdown(); |
|
104 |
|
105 /** |
|
106 * Put an accessible event into the queue to process it later. |
|
107 */ |
|
108 void QueueEvent(AccEvent* aEvent) |
|
109 { |
|
110 if (PushEvent(aEvent)) |
|
111 ScheduleProcessing(); |
|
112 } |
|
113 |
|
114 /** |
|
115 * Schedule binding the child document to the tree of this document. |
|
116 */ |
|
117 void ScheduleChildDocBinding(DocAccessible* aDocument); |
|
118 |
|
119 /** |
|
120 * Schedule the accessible tree update because of rendered text changes. |
|
121 */ |
|
122 inline void ScheduleTextUpdate(nsIContent* aTextNode) |
|
123 { |
|
124 if (mTextHash.PutEntry(aTextNode)) |
|
125 ScheduleProcessing(); |
|
126 } |
|
127 |
|
128 /** |
|
129 * Pend accessible tree update for content insertion. |
|
130 */ |
|
131 void ScheduleContentInsertion(Accessible* aContainer, |
|
132 nsIContent* aStartChildNode, |
|
133 nsIContent* aEndChildNode); |
|
134 |
|
135 /** |
|
136 * Process the generic notification synchronously if there are no pending |
|
137 * layout changes and no notifications are pending or being processed right |
|
138 * now. Otherwise, queue it up to process asynchronously. |
|
139 * |
|
140 * @note The caller must guarantee that the given instance still exists when |
|
141 * the notification is processed. |
|
142 */ |
|
143 template<class Class, class Arg> |
|
144 inline void HandleNotification(Class* aInstance, |
|
145 typename TNotification<Class, Arg>::Callback aMethod, |
|
146 Arg* aArg) |
|
147 { |
|
148 if (!IsUpdatePending()) { |
|
149 #ifdef A11Y_LOG |
|
150 if (mozilla::a11y::logging::IsEnabled(mozilla::a11y::logging::eNotifications)) |
|
151 mozilla::a11y::logging::Text("sync notification processing"); |
|
152 #endif |
|
153 (aInstance->*aMethod)(aArg); |
|
154 return; |
|
155 } |
|
156 |
|
157 nsRefPtr<Notification> notification = |
|
158 new TNotification<Class, Arg>(aInstance, aMethod, aArg); |
|
159 if (notification && mNotifications.AppendElement(notification)) |
|
160 ScheduleProcessing(); |
|
161 } |
|
162 |
|
163 /** |
|
164 * Schedule the generic notification to process asynchronously. |
|
165 * |
|
166 * @note The caller must guarantee that the given instance still exists when |
|
167 * the notification is processed. |
|
168 */ |
|
169 template<class Class, class Arg> |
|
170 inline void ScheduleNotification(Class* aInstance, |
|
171 typename TNotification<Class, Arg>::Callback aMethod, |
|
172 Arg* aArg) |
|
173 { |
|
174 nsRefPtr<Notification> notification = |
|
175 new TNotification<Class, Arg>(aInstance, aMethod, aArg); |
|
176 if (notification && mNotifications.AppendElement(notification)) |
|
177 ScheduleProcessing(); |
|
178 } |
|
179 |
|
180 #ifdef DEBUG |
|
181 bool IsUpdating() const |
|
182 { return mObservingState == eRefreshProcessingForUpdate; } |
|
183 #endif |
|
184 |
|
185 protected: |
|
186 nsCycleCollectingAutoRefCnt mRefCnt; |
|
187 NS_DECL_OWNINGTHREAD |
|
188 |
|
189 /** |
|
190 * Start to observe refresh to make notifications and events processing after |
|
191 * layout. |
|
192 */ |
|
193 void ScheduleProcessing(); |
|
194 |
|
195 /** |
|
196 * Return true if the accessible tree state update is pending. |
|
197 */ |
|
198 bool IsUpdatePending(); |
|
199 |
|
200 private: |
|
201 NotificationController(const NotificationController&); |
|
202 NotificationController& operator = (const NotificationController&); |
|
203 |
|
204 // nsARefreshObserver |
|
205 virtual void WillRefresh(mozilla::TimeStamp aTime); |
|
206 |
|
207 private: |
|
208 /** |
|
209 * Indicates whether we're waiting on an event queue processing from our |
|
210 * notification controller to flush events. |
|
211 */ |
|
212 enum eObservingState { |
|
213 eNotObservingRefresh, |
|
214 eRefreshObserving, |
|
215 eRefreshProcessing, |
|
216 eRefreshProcessingForUpdate |
|
217 }; |
|
218 eObservingState mObservingState; |
|
219 |
|
220 /** |
|
221 * The presshell of the document accessible. |
|
222 */ |
|
223 nsIPresShell* mPresShell; |
|
224 |
|
225 /** |
|
226 * Child documents that needs to be bound to the tree. |
|
227 */ |
|
228 nsTArray<nsRefPtr<DocAccessible> > mHangingChildDocuments; |
|
229 |
|
230 /** |
|
231 * Storage for content inserted notification information. |
|
232 */ |
|
233 class ContentInsertion |
|
234 { |
|
235 public: |
|
236 ContentInsertion(DocAccessible* aDocument, Accessible* aContainer); |
|
237 virtual ~ContentInsertion() { mDocument = nullptr; } |
|
238 |
|
239 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ContentInsertion) |
|
240 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ContentInsertion) |
|
241 |
|
242 bool InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode); |
|
243 void Process(); |
|
244 |
|
245 private: |
|
246 ContentInsertion(); |
|
247 ContentInsertion(const ContentInsertion&); |
|
248 ContentInsertion& operator = (const ContentInsertion&); |
|
249 |
|
250 // The document used to process content insertion, matched to document of |
|
251 // the notification controller that this notification belongs to, therefore |
|
252 // it's ok to keep it as weak ref. |
|
253 DocAccessible* mDocument; |
|
254 |
|
255 // The container accessible that content insertion occurs within. |
|
256 nsRefPtr<Accessible> mContainer; |
|
257 |
|
258 // Array of inserted contents. |
|
259 nsTArray<nsCOMPtr<nsIContent> > mInsertedContent; |
|
260 }; |
|
261 |
|
262 /** |
|
263 * A pending accessible tree update notifications for content insertions. |
|
264 * Don't make this an nsAutoTArray; we use SwapElements() on it. |
|
265 */ |
|
266 nsTArray<nsRefPtr<ContentInsertion> > mContentInsertions; |
|
267 |
|
268 template<class T> |
|
269 class nsCOMPtrHashKey : public PLDHashEntryHdr |
|
270 { |
|
271 public: |
|
272 typedef T* KeyType; |
|
273 typedef const T* KeyTypePointer; |
|
274 |
|
275 nsCOMPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {} |
|
276 nsCOMPtrHashKey(const nsPtrHashKey<T> &aToCopy) : mKey(aToCopy.mKey) {} |
|
277 ~nsCOMPtrHashKey() { } |
|
278 |
|
279 KeyType GetKey() const { return mKey; } |
|
280 bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; } |
|
281 |
|
282 static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } |
|
283 static PLDHashNumber HashKey(KeyTypePointer aKey) |
|
284 { return NS_PTR_TO_INT32(aKey) >> 2; } |
|
285 |
|
286 enum { ALLOW_MEMMOVE = true }; |
|
287 |
|
288 protected: |
|
289 nsCOMPtr<T> mKey; |
|
290 }; |
|
291 |
|
292 /** |
|
293 * A pending accessible tree update notifications for rendered text changes. |
|
294 */ |
|
295 nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash; |
|
296 |
|
297 /** |
|
298 * Update the accessible tree for pending rendered text change notifications. |
|
299 */ |
|
300 static PLDHashOperator TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry, |
|
301 void* aUserArg); |
|
302 |
|
303 /** |
|
304 * Other notifications like DOM events. Don't make this an nsAutoTArray; we |
|
305 * use SwapElements() on it. |
|
306 */ |
|
307 nsTArray<nsRefPtr<Notification> > mNotifications; |
|
308 }; |
|
309 |
|
310 } // namespace a11y |
|
311 } // namespace mozilla |
|
312 |
|
313 #endif // mozilla_a11y_NotificationController_h_ |