1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2010
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Alexander Surkov <surkov.alexander@gmail.com> (original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #ifndef NotificationController_h_
40 : #define NotificationController_h_
41 :
42 : #include "AccEvent.h"
43 : #include "nsCycleCollectionParticipant.h"
44 : #include "nsRefreshDriver.h"
45 :
46 : class nsAccessible;
47 : class nsDocAccessible;
48 : class nsIContent;
49 :
50 : // Uncomment to log notifications processing.
51 : //#define DEBUG_NOTIFICATIONS
52 :
53 : #ifdef DEBUG_NOTIFICATIONS
54 : #define DEBUG_CONTENTMUTATION
55 : #define DEBUG_TEXTCHANGE
56 : #endif
57 :
58 : /**
59 : * Notification interface.
60 : */
61 : class Notification
62 : {
63 : public:
64 0 : virtual ~Notification() { };
65 :
66 0 : NS_INLINE_DECL_REFCOUNTING(Notification)
67 :
68 : /**
69 : * Process notification.
70 : */
71 : virtual void Process() = 0;
72 :
73 : protected:
74 0 : Notification() { }
75 :
76 : private:
77 : Notification(const Notification&);
78 : Notification& operator = (const Notification&);
79 : };
80 :
81 :
82 : /**
83 : * Template class for generic notification.
84 : *
85 : * @note Instance is kept as a weak ref, the caller must guarantee it exists
86 : * longer than the document accessible owning the notification controller
87 : * that this notification is processed by.
88 : */
89 : template<class Class, class Arg>
90 : class TNotification : public Notification
91 : {
92 : public:
93 : typedef void (Class::*Callback)(Arg*);
94 :
95 0 : TNotification(Class* aInstance, Callback aCallback, Arg* aArg) :
96 0 : mInstance(aInstance), mCallback(aCallback), mArg(aArg) { }
97 0 : virtual ~TNotification() { mInstance = nsnull; }
98 :
99 0 : virtual void Process()
100 : {
101 0 : (mInstance->*mCallback)(mArg);
102 :
103 0 : mInstance = nsnull;
104 0 : mCallback = nsnull;
105 0 : mArg = nsnull;
106 0 : }
107 :
108 : private:
109 : TNotification(const TNotification&);
110 : TNotification& operator = (const TNotification&);
111 :
112 : Class* mInstance;
113 : Callback mCallback;
114 : nsCOMPtr<Arg> mArg;
115 : };
116 :
117 : /**
118 : * Used to process notifications from core for the document accessible.
119 : */
120 : class NotificationController : public nsARefreshObserver
121 : {
122 : public:
123 : NotificationController(nsDocAccessible* aDocument, nsIPresShell* aPresShell);
124 : virtual ~NotificationController();
125 :
126 : NS_IMETHOD_(nsrefcnt) AddRef(void);
127 : NS_IMETHOD_(nsrefcnt) Release(void);
128 :
129 1464 : NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController)
130 :
131 : /**
132 : * Shutdown the notification controller.
133 : */
134 : void Shutdown();
135 :
136 : /**
137 : * Put an accessible event into the queue to process it later.
138 : */
139 : void QueueEvent(AccEvent* aEvent);
140 :
141 : /**
142 : * Schedule binding the child document to the tree of this document.
143 : */
144 : void ScheduleChildDocBinding(nsDocAccessible* aDocument);
145 :
146 : /**
147 : * Schedule the accessible tree update because of rendered text changes.
148 : */
149 0 : inline void ScheduleTextUpdate(nsIContent* aTextNode)
150 : {
151 0 : if (mTextHash.PutEntry(aTextNode))
152 0 : ScheduleProcessing();
153 0 : }
154 :
155 : /**
156 : * Pend accessible tree update for content insertion.
157 : */
158 : void ScheduleContentInsertion(nsAccessible* aContainer,
159 : nsIContent* aStartChildNode,
160 : nsIContent* aEndChildNode);
161 :
162 : /**
163 : * Process the generic notification synchronously if there are no pending
164 : * layout changes and no notifications are pending or being processed right
165 : * now. Otherwise, queue it up to process asynchronously.
166 : *
167 : * @note The caller must guarantee that the given instance still exists when
168 : * the notification is processed.
169 : */
170 : template<class Class, class Arg>
171 0 : inline void HandleNotification(Class* aInstance,
172 : typename TNotification<Class, Arg>::Callback aMethod,
173 : Arg* aArg)
174 : {
175 0 : if (!IsUpdatePending()) {
176 : #ifdef DEBUG_NOTIFICATIONS
177 : printf("\nsync notification processing\n");
178 : #endif
179 0 : (aInstance->*aMethod)(aArg);
180 0 : return;
181 : }
182 :
183 : nsRefPtr<Notification> notification =
184 0 : new TNotification<Class, Arg>(aInstance, aMethod, aArg);
185 0 : if (notification && mNotifications.AppendElement(notification))
186 0 : ScheduleProcessing();
187 : }
188 :
189 : /**
190 : * Schedule the generic notification to process asynchronously.
191 : *
192 : * @note The caller must guarantee that the given instance still exists when
193 : * the notification is processed.
194 : */
195 : template<class Class, class Arg>
196 0 : inline void ScheduleNotification(Class* aInstance,
197 : typename TNotification<Class, Arg>::Callback aMethod,
198 : Arg* aArg)
199 : {
200 : nsRefPtr<Notification> notification =
201 0 : new TNotification<Class, Arg>(aInstance, aMethod, aArg);
202 0 : if (notification && mNotifications.AppendElement(notification))
203 0 : ScheduleProcessing();
204 0 : }
205 :
206 : #ifdef DEBUG
207 0 : bool IsUpdating() const
208 0 : { return mObservingState == eRefreshProcessingForUpdate; }
209 : #endif
210 :
211 : protected:
212 : nsAutoRefCnt mRefCnt;
213 : NS_DECL_OWNINGTHREAD
214 :
215 : /**
216 : * Start to observe refresh to make notifications and events processing after
217 : * layout.
218 : */
219 : void ScheduleProcessing();
220 :
221 : /**
222 : * Return true if the accessible tree state update is pending.
223 : */
224 : bool IsUpdatePending();
225 :
226 : private:
227 : NotificationController(const NotificationController&);
228 : NotificationController& operator = (const NotificationController&);
229 :
230 : // nsARefreshObserver
231 : virtual void WillRefresh(mozilla::TimeStamp aTime);
232 :
233 : // Event queue processing
234 : /**
235 : * Coalesce redundant events from the queue.
236 : */
237 : void CoalesceEvents();
238 :
239 : /**
240 : * Apply aEventRule to same type event that from sibling nodes of aDOMNode.
241 : * @param aEventsToFire array of pending events
242 : * @param aStart start index of pending events to be scanned
243 : * @param aEnd end index to be scanned (not included)
244 : * @param aEventType target event type
245 : * @param aDOMNode target are siblings of this node
246 : * @param aEventRule the event rule to be applied
247 : * (should be eDoNotEmit or eAllowDupes)
248 : */
249 : void ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
250 : PRUint32 aEventType, nsINode* aNode,
251 : AccEvent::EEventRule aEventRule);
252 :
253 : /**
254 : * Coalesce two selection change events within the same select control.
255 : */
256 : void CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
257 : AccSelChangeEvent* aThisEvent,
258 : PRInt32 aThisIndex);
259 :
260 : /**
261 : * Coalesce text change events caused by sibling hide events.
262 : */
263 : void CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
264 : AccHideEvent* aThisEvent);
265 : void CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
266 : AccShowEvent* aThisEvent);
267 :
268 : /**
269 : * Create text change event caused by hide or show event. When a node is
270 : * hidden/removed or shown/appended, the text in an ancestor hyper text will
271 : * lose or get new characters.
272 : */
273 : void CreateTextChangeEventFor(AccMutationEvent* aEvent);
274 :
275 : private:
276 : /**
277 : * Indicates whether we're waiting on an event queue processing from our
278 : * notification controller to flush events.
279 : */
280 : enum eObservingState {
281 : eNotObservingRefresh,
282 : eRefreshObserving,
283 : eRefreshProcessingForUpdate
284 : };
285 : eObservingState mObservingState;
286 :
287 : /**
288 : * The document accessible reference owning this queue.
289 : */
290 : nsRefPtr<nsDocAccessible> mDocument;
291 :
292 : /**
293 : * The presshell of the document accessible.
294 : */
295 : nsIPresShell* mPresShell;
296 :
297 : /**
298 : * Child documents that needs to be bound to the tree.
299 : */
300 : nsTArray<nsRefPtr<nsDocAccessible> > mHangingChildDocuments;
301 :
302 : /**
303 : * Storage for content inserted notification information.
304 : */
305 : class ContentInsertion
306 : {
307 : public:
308 : ContentInsertion(nsDocAccessible* aDocument, nsAccessible* aContainer);
309 0 : virtual ~ContentInsertion() { mDocument = nsnull; }
310 :
311 0 : NS_INLINE_DECL_REFCOUNTING(ContentInsertion)
312 1464 : NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ContentInsertion)
313 :
314 : bool InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode);
315 : void Process();
316 :
317 : private:
318 : ContentInsertion();
319 : ContentInsertion(const ContentInsertion&);
320 : ContentInsertion& operator = (const ContentInsertion&);
321 :
322 : // The document used to process content insertion, matched to document of
323 : // the notification controller that this notification belongs to, therefore
324 : // it's ok to keep it as weak ref.
325 : nsDocAccessible* mDocument;
326 :
327 : // The container accessible that content insertion occurs within.
328 : nsRefPtr<nsAccessible> mContainer;
329 :
330 : // Array of inserted contents.
331 : nsTArray<nsCOMPtr<nsIContent> > mInsertedContent;
332 : };
333 :
334 : /**
335 : * A pending accessible tree update notifications for content insertions.
336 : * Don't make this an nsAutoTArray; we use SwapElements() on it.
337 : */
338 : nsTArray<nsRefPtr<ContentInsertion> > mContentInsertions;
339 :
340 : template<class T>
341 : class nsCOMPtrHashKey : public PLDHashEntryHdr
342 : {
343 : public:
344 : typedef T* KeyType;
345 : typedef const T* KeyTypePointer;
346 :
347 0 : nsCOMPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
348 : nsCOMPtrHashKey(const nsPtrHashKey<T> &aToCopy) : mKey(aToCopy.mKey) {}
349 0 : ~nsCOMPtrHashKey() { }
350 :
351 0 : KeyType GetKey() const { return mKey; }
352 0 : bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
353 :
354 0 : static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
355 0 : static PLDHashNumber HashKey(KeyTypePointer aKey)
356 0 : { return NS_PTR_TO_INT32(aKey) >> 2; }
357 :
358 : enum { ALLOW_MEMMOVE = true };
359 :
360 : protected:
361 : nsCOMPtr<T> mKey;
362 : };
363 :
364 : /**
365 : * A pending accessible tree update notifications for rendered text changes.
366 : */
367 : nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash;
368 :
369 : /**
370 : * Update the accessible tree for pending rendered text change notifications.
371 : */
372 : static PLDHashOperator TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
373 : void* aUserArg);
374 :
375 : /**
376 : * Other notifications like DOM events. Don't make this an nsAutoTArray; we
377 : * use SwapElements() on it.
378 : */
379 : nsTArray<nsRefPtr<Notification> > mNotifications;
380 :
381 : /**
382 : * Pending events array. Don't make this an nsAutoTArray; we use
383 : * SwapElements() on it.
384 : */
385 : nsTArray<nsRefPtr<AccEvent> > mEvents;
386 : };
387 :
388 : #endif
|