1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/accessible/src/base/NotificationController.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,313 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef mozilla_a11y_NotificationController_h_ 1.10 +#define mozilla_a11y_NotificationController_h_ 1.11 + 1.12 +#include "EventQueue.h" 1.13 + 1.14 +#include "nsCycleCollectionParticipant.h" 1.15 +#include "nsRefreshDriver.h" 1.16 + 1.17 +#ifdef A11Y_LOG 1.18 +#include "Logging.h" 1.19 +#endif 1.20 + 1.21 +namespace mozilla { 1.22 +namespace a11y { 1.23 + 1.24 +class DocAccessible; 1.25 + 1.26 +/** 1.27 + * Notification interface. 1.28 + */ 1.29 +class Notification 1.30 +{ 1.31 +public: 1.32 + NS_INLINE_DECL_REFCOUNTING(Notification) 1.33 + 1.34 + /** 1.35 + * Process notification. 1.36 + */ 1.37 + virtual void Process() = 0; 1.38 + 1.39 +protected: 1.40 + Notification() { } 1.41 + 1.42 + /** 1.43 + * Protected destructor, to discourage deletion outside of Release(): 1.44 + */ 1.45 + virtual ~Notification() { } 1.46 + 1.47 +private: 1.48 + Notification(const Notification&); 1.49 + Notification& operator = (const Notification&); 1.50 +}; 1.51 + 1.52 + 1.53 +/** 1.54 + * Template class for generic notification. 1.55 + * 1.56 + * @note Instance is kept as a weak ref, the caller must guarantee it exists 1.57 + * longer than the document accessible owning the notification controller 1.58 + * that this notification is processed by. 1.59 + */ 1.60 +template<class Class, class Arg> 1.61 +class TNotification : public Notification 1.62 +{ 1.63 +public: 1.64 + typedef void (Class::*Callback)(Arg*); 1.65 + 1.66 + TNotification(Class* aInstance, Callback aCallback, Arg* aArg) : 1.67 + mInstance(aInstance), mCallback(aCallback), mArg(aArg) { } 1.68 + virtual ~TNotification() { mInstance = nullptr; } 1.69 + 1.70 + virtual void Process() 1.71 + { 1.72 + (mInstance->*mCallback)(mArg); 1.73 + 1.74 + mInstance = nullptr; 1.75 + mCallback = nullptr; 1.76 + mArg = nullptr; 1.77 + } 1.78 + 1.79 +private: 1.80 + TNotification(const TNotification&); 1.81 + TNotification& operator = (const TNotification&); 1.82 + 1.83 + Class* mInstance; 1.84 + Callback mCallback; 1.85 + nsRefPtr<Arg> mArg; 1.86 +}; 1.87 + 1.88 +/** 1.89 + * Used to process notifications from core for the document accessible. 1.90 + */ 1.91 +class NotificationController : public EventQueue, 1.92 + public nsARefreshObserver 1.93 +{ 1.94 +public: 1.95 + NotificationController(DocAccessible* aDocument, nsIPresShell* aPresShell); 1.96 + virtual ~NotificationController(); 1.97 + 1.98 + NS_IMETHOD_(MozExternalRefCountType) AddRef(void); 1.99 + NS_IMETHOD_(MozExternalRefCountType) Release(void); 1.100 + 1.101 + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController) 1.102 + 1.103 + /** 1.104 + * Shutdown the notification controller. 1.105 + */ 1.106 + void Shutdown(); 1.107 + 1.108 + /** 1.109 + * Put an accessible event into the queue to process it later. 1.110 + */ 1.111 + void QueueEvent(AccEvent* aEvent) 1.112 + { 1.113 + if (PushEvent(aEvent)) 1.114 + ScheduleProcessing(); 1.115 + } 1.116 + 1.117 + /** 1.118 + * Schedule binding the child document to the tree of this document. 1.119 + */ 1.120 + void ScheduleChildDocBinding(DocAccessible* aDocument); 1.121 + 1.122 + /** 1.123 + * Schedule the accessible tree update because of rendered text changes. 1.124 + */ 1.125 + inline void ScheduleTextUpdate(nsIContent* aTextNode) 1.126 + { 1.127 + if (mTextHash.PutEntry(aTextNode)) 1.128 + ScheduleProcessing(); 1.129 + } 1.130 + 1.131 + /** 1.132 + * Pend accessible tree update for content insertion. 1.133 + */ 1.134 + void ScheduleContentInsertion(Accessible* aContainer, 1.135 + nsIContent* aStartChildNode, 1.136 + nsIContent* aEndChildNode); 1.137 + 1.138 + /** 1.139 + * Process the generic notification synchronously if there are no pending 1.140 + * layout changes and no notifications are pending or being processed right 1.141 + * now. Otherwise, queue it up to process asynchronously. 1.142 + * 1.143 + * @note The caller must guarantee that the given instance still exists when 1.144 + * the notification is processed. 1.145 + */ 1.146 + template<class Class, class Arg> 1.147 + inline void HandleNotification(Class* aInstance, 1.148 + typename TNotification<Class, Arg>::Callback aMethod, 1.149 + Arg* aArg) 1.150 + { 1.151 + if (!IsUpdatePending()) { 1.152 +#ifdef A11Y_LOG 1.153 + if (mozilla::a11y::logging::IsEnabled(mozilla::a11y::logging::eNotifications)) 1.154 + mozilla::a11y::logging::Text("sync notification processing"); 1.155 +#endif 1.156 + (aInstance->*aMethod)(aArg); 1.157 + return; 1.158 + } 1.159 + 1.160 + nsRefPtr<Notification> notification = 1.161 + new TNotification<Class, Arg>(aInstance, aMethod, aArg); 1.162 + if (notification && mNotifications.AppendElement(notification)) 1.163 + ScheduleProcessing(); 1.164 + } 1.165 + 1.166 + /** 1.167 + * Schedule the generic notification to process asynchronously. 1.168 + * 1.169 + * @note The caller must guarantee that the given instance still exists when 1.170 + * the notification is processed. 1.171 + */ 1.172 + template<class Class, class Arg> 1.173 + inline void ScheduleNotification(Class* aInstance, 1.174 + typename TNotification<Class, Arg>::Callback aMethod, 1.175 + Arg* aArg) 1.176 + { 1.177 + nsRefPtr<Notification> notification = 1.178 + new TNotification<Class, Arg>(aInstance, aMethod, aArg); 1.179 + if (notification && mNotifications.AppendElement(notification)) 1.180 + ScheduleProcessing(); 1.181 + } 1.182 + 1.183 +#ifdef DEBUG 1.184 + bool IsUpdating() const 1.185 + { return mObservingState == eRefreshProcessingForUpdate; } 1.186 +#endif 1.187 + 1.188 +protected: 1.189 + nsCycleCollectingAutoRefCnt mRefCnt; 1.190 + NS_DECL_OWNINGTHREAD 1.191 + 1.192 + /** 1.193 + * Start to observe refresh to make notifications and events processing after 1.194 + * layout. 1.195 + */ 1.196 + void ScheduleProcessing(); 1.197 + 1.198 + /** 1.199 + * Return true if the accessible tree state update is pending. 1.200 + */ 1.201 + bool IsUpdatePending(); 1.202 + 1.203 +private: 1.204 + NotificationController(const NotificationController&); 1.205 + NotificationController& operator = (const NotificationController&); 1.206 + 1.207 + // nsARefreshObserver 1.208 + virtual void WillRefresh(mozilla::TimeStamp aTime); 1.209 + 1.210 +private: 1.211 + /** 1.212 + * Indicates whether we're waiting on an event queue processing from our 1.213 + * notification controller to flush events. 1.214 + */ 1.215 + enum eObservingState { 1.216 + eNotObservingRefresh, 1.217 + eRefreshObserving, 1.218 + eRefreshProcessing, 1.219 + eRefreshProcessingForUpdate 1.220 + }; 1.221 + eObservingState mObservingState; 1.222 + 1.223 + /** 1.224 + * The presshell of the document accessible. 1.225 + */ 1.226 + nsIPresShell* mPresShell; 1.227 + 1.228 + /** 1.229 + * Child documents that needs to be bound to the tree. 1.230 + */ 1.231 + nsTArray<nsRefPtr<DocAccessible> > mHangingChildDocuments; 1.232 + 1.233 + /** 1.234 + * Storage for content inserted notification information. 1.235 + */ 1.236 + class ContentInsertion 1.237 + { 1.238 + public: 1.239 + ContentInsertion(DocAccessible* aDocument, Accessible* aContainer); 1.240 + virtual ~ContentInsertion() { mDocument = nullptr; } 1.241 + 1.242 + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ContentInsertion) 1.243 + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ContentInsertion) 1.244 + 1.245 + bool InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode); 1.246 + void Process(); 1.247 + 1.248 + private: 1.249 + ContentInsertion(); 1.250 + ContentInsertion(const ContentInsertion&); 1.251 + ContentInsertion& operator = (const ContentInsertion&); 1.252 + 1.253 + // The document used to process content insertion, matched to document of 1.254 + // the notification controller that this notification belongs to, therefore 1.255 + // it's ok to keep it as weak ref. 1.256 + DocAccessible* mDocument; 1.257 + 1.258 + // The container accessible that content insertion occurs within. 1.259 + nsRefPtr<Accessible> mContainer; 1.260 + 1.261 + // Array of inserted contents. 1.262 + nsTArray<nsCOMPtr<nsIContent> > mInsertedContent; 1.263 + }; 1.264 + 1.265 + /** 1.266 + * A pending accessible tree update notifications for content insertions. 1.267 + * Don't make this an nsAutoTArray; we use SwapElements() on it. 1.268 + */ 1.269 + nsTArray<nsRefPtr<ContentInsertion> > mContentInsertions; 1.270 + 1.271 + template<class T> 1.272 + class nsCOMPtrHashKey : public PLDHashEntryHdr 1.273 + { 1.274 + public: 1.275 + typedef T* KeyType; 1.276 + typedef const T* KeyTypePointer; 1.277 + 1.278 + nsCOMPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {} 1.279 + nsCOMPtrHashKey(const nsPtrHashKey<T> &aToCopy) : mKey(aToCopy.mKey) {} 1.280 + ~nsCOMPtrHashKey() { } 1.281 + 1.282 + KeyType GetKey() const { return mKey; } 1.283 + bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; } 1.284 + 1.285 + static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } 1.286 + static PLDHashNumber HashKey(KeyTypePointer aKey) 1.287 + { return NS_PTR_TO_INT32(aKey) >> 2; } 1.288 + 1.289 + enum { ALLOW_MEMMOVE = true }; 1.290 + 1.291 + protected: 1.292 + nsCOMPtr<T> mKey; 1.293 + }; 1.294 + 1.295 + /** 1.296 + * A pending accessible tree update notifications for rendered text changes. 1.297 + */ 1.298 + nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash; 1.299 + 1.300 + /** 1.301 + * Update the accessible tree for pending rendered text change notifications. 1.302 + */ 1.303 + static PLDHashOperator TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry, 1.304 + void* aUserArg); 1.305 + 1.306 + /** 1.307 + * Other notifications like DOM events. Don't make this an nsAutoTArray; we 1.308 + * use SwapElements() on it. 1.309 + */ 1.310 + nsTArray<nsRefPtr<Notification> > mNotifications; 1.311 +}; 1.312 + 1.313 +} // namespace a11y 1.314 +} // namespace mozilla 1.315 + 1.316 +#endif // mozilla_a11y_NotificationController_h_