michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_a11y_NotificationController_h_ michael@0: #define mozilla_a11y_NotificationController_h_ michael@0: michael@0: #include "EventQueue.h" michael@0: michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsRefreshDriver.h" michael@0: michael@0: #ifdef A11Y_LOG michael@0: #include "Logging.h" michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace a11y { michael@0: michael@0: class DocAccessible; michael@0: michael@0: /** michael@0: * Notification interface. michael@0: */ michael@0: class Notification michael@0: { michael@0: public: michael@0: NS_INLINE_DECL_REFCOUNTING(Notification) michael@0: michael@0: /** michael@0: * Process notification. michael@0: */ michael@0: virtual void Process() = 0; michael@0: michael@0: protected: michael@0: Notification() { } michael@0: michael@0: /** michael@0: * Protected destructor, to discourage deletion outside of Release(): michael@0: */ michael@0: virtual ~Notification() { } michael@0: michael@0: private: michael@0: Notification(const Notification&); michael@0: Notification& operator = (const Notification&); michael@0: }; michael@0: michael@0: michael@0: /** michael@0: * Template class for generic notification. michael@0: * michael@0: * @note Instance is kept as a weak ref, the caller must guarantee it exists michael@0: * longer than the document accessible owning the notification controller michael@0: * that this notification is processed by. michael@0: */ michael@0: template michael@0: class TNotification : public Notification michael@0: { michael@0: public: michael@0: typedef void (Class::*Callback)(Arg*); michael@0: michael@0: TNotification(Class* aInstance, Callback aCallback, Arg* aArg) : michael@0: mInstance(aInstance), mCallback(aCallback), mArg(aArg) { } michael@0: virtual ~TNotification() { mInstance = nullptr; } michael@0: michael@0: virtual void Process() michael@0: { michael@0: (mInstance->*mCallback)(mArg); michael@0: michael@0: mInstance = nullptr; michael@0: mCallback = nullptr; michael@0: mArg = nullptr; michael@0: } michael@0: michael@0: private: michael@0: TNotification(const TNotification&); michael@0: TNotification& operator = (const TNotification&); michael@0: michael@0: Class* mInstance; michael@0: Callback mCallback; michael@0: nsRefPtr mArg; michael@0: }; michael@0: michael@0: /** michael@0: * Used to process notifications from core for the document accessible. michael@0: */ michael@0: class NotificationController : public EventQueue, michael@0: public nsARefreshObserver michael@0: { michael@0: public: michael@0: NotificationController(DocAccessible* aDocument, nsIPresShell* aPresShell); michael@0: virtual ~NotificationController(); michael@0: michael@0: NS_IMETHOD_(MozExternalRefCountType) AddRef(void); michael@0: NS_IMETHOD_(MozExternalRefCountType) Release(void); michael@0: michael@0: NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController) michael@0: michael@0: /** michael@0: * Shutdown the notification controller. michael@0: */ michael@0: void Shutdown(); michael@0: michael@0: /** michael@0: * Put an accessible event into the queue to process it later. michael@0: */ michael@0: void QueueEvent(AccEvent* aEvent) michael@0: { michael@0: if (PushEvent(aEvent)) michael@0: ScheduleProcessing(); michael@0: } michael@0: michael@0: /** michael@0: * Schedule binding the child document to the tree of this document. michael@0: */ michael@0: void ScheduleChildDocBinding(DocAccessible* aDocument); michael@0: michael@0: /** michael@0: * Schedule the accessible tree update because of rendered text changes. michael@0: */ michael@0: inline void ScheduleTextUpdate(nsIContent* aTextNode) michael@0: { michael@0: if (mTextHash.PutEntry(aTextNode)) michael@0: ScheduleProcessing(); michael@0: } michael@0: michael@0: /** michael@0: * Pend accessible tree update for content insertion. michael@0: */ michael@0: void ScheduleContentInsertion(Accessible* aContainer, michael@0: nsIContent* aStartChildNode, michael@0: nsIContent* aEndChildNode); michael@0: michael@0: /** michael@0: * Process the generic notification synchronously if there are no pending michael@0: * layout changes and no notifications are pending or being processed right michael@0: * now. Otherwise, queue it up to process asynchronously. michael@0: * michael@0: * @note The caller must guarantee that the given instance still exists when michael@0: * the notification is processed. michael@0: */ michael@0: template michael@0: inline void HandleNotification(Class* aInstance, michael@0: typename TNotification::Callback aMethod, michael@0: Arg* aArg) michael@0: { michael@0: if (!IsUpdatePending()) { michael@0: #ifdef A11Y_LOG michael@0: if (mozilla::a11y::logging::IsEnabled(mozilla::a11y::logging::eNotifications)) michael@0: mozilla::a11y::logging::Text("sync notification processing"); michael@0: #endif michael@0: (aInstance->*aMethod)(aArg); michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr notification = michael@0: new TNotification(aInstance, aMethod, aArg); michael@0: if (notification && mNotifications.AppendElement(notification)) michael@0: ScheduleProcessing(); michael@0: } michael@0: michael@0: /** michael@0: * Schedule the generic notification to process asynchronously. michael@0: * michael@0: * @note The caller must guarantee that the given instance still exists when michael@0: * the notification is processed. michael@0: */ michael@0: template michael@0: inline void ScheduleNotification(Class* aInstance, michael@0: typename TNotification::Callback aMethod, michael@0: Arg* aArg) michael@0: { michael@0: nsRefPtr notification = michael@0: new TNotification(aInstance, aMethod, aArg); michael@0: if (notification && mNotifications.AppendElement(notification)) michael@0: ScheduleProcessing(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: bool IsUpdating() const michael@0: { return mObservingState == eRefreshProcessingForUpdate; } michael@0: #endif michael@0: michael@0: protected: michael@0: nsCycleCollectingAutoRefCnt mRefCnt; michael@0: NS_DECL_OWNINGTHREAD michael@0: michael@0: /** michael@0: * Start to observe refresh to make notifications and events processing after michael@0: * layout. michael@0: */ michael@0: void ScheduleProcessing(); michael@0: michael@0: /** michael@0: * Return true if the accessible tree state update is pending. michael@0: */ michael@0: bool IsUpdatePending(); michael@0: michael@0: private: michael@0: NotificationController(const NotificationController&); michael@0: NotificationController& operator = (const NotificationController&); michael@0: michael@0: // nsARefreshObserver michael@0: virtual void WillRefresh(mozilla::TimeStamp aTime); michael@0: michael@0: private: michael@0: /** michael@0: * Indicates whether we're waiting on an event queue processing from our michael@0: * notification controller to flush events. michael@0: */ michael@0: enum eObservingState { michael@0: eNotObservingRefresh, michael@0: eRefreshObserving, michael@0: eRefreshProcessing, michael@0: eRefreshProcessingForUpdate michael@0: }; michael@0: eObservingState mObservingState; michael@0: michael@0: /** michael@0: * The presshell of the document accessible. michael@0: */ michael@0: nsIPresShell* mPresShell; michael@0: michael@0: /** michael@0: * Child documents that needs to be bound to the tree. michael@0: */ michael@0: nsTArray > mHangingChildDocuments; michael@0: michael@0: /** michael@0: * Storage for content inserted notification information. michael@0: */ michael@0: class ContentInsertion michael@0: { michael@0: public: michael@0: ContentInsertion(DocAccessible* aDocument, Accessible* aContainer); michael@0: virtual ~ContentInsertion() { mDocument = nullptr; } michael@0: michael@0: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ContentInsertion) michael@0: NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ContentInsertion) michael@0: michael@0: bool InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode); michael@0: void Process(); michael@0: michael@0: private: michael@0: ContentInsertion(); michael@0: ContentInsertion(const ContentInsertion&); michael@0: ContentInsertion& operator = (const ContentInsertion&); michael@0: michael@0: // The document used to process content insertion, matched to document of michael@0: // the notification controller that this notification belongs to, therefore michael@0: // it's ok to keep it as weak ref. michael@0: DocAccessible* mDocument; michael@0: michael@0: // The container accessible that content insertion occurs within. michael@0: nsRefPtr mContainer; michael@0: michael@0: // Array of inserted contents. michael@0: nsTArray > mInsertedContent; michael@0: }; michael@0: michael@0: /** michael@0: * A pending accessible tree update notifications for content insertions. michael@0: * Don't make this an nsAutoTArray; we use SwapElements() on it. michael@0: */ michael@0: nsTArray > mContentInsertions; michael@0: michael@0: template michael@0: class nsCOMPtrHashKey : public PLDHashEntryHdr michael@0: { michael@0: public: michael@0: typedef T* KeyType; michael@0: typedef const T* KeyTypePointer; michael@0: michael@0: nsCOMPtrHashKey(const T* aKey) : mKey(const_cast(aKey)) {} michael@0: nsCOMPtrHashKey(const nsPtrHashKey &aToCopy) : mKey(aToCopy.mKey) {} michael@0: ~nsCOMPtrHashKey() { } michael@0: michael@0: KeyType GetKey() const { return mKey; } michael@0: bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; } michael@0: michael@0: static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } michael@0: static PLDHashNumber HashKey(KeyTypePointer aKey) michael@0: { return NS_PTR_TO_INT32(aKey) >> 2; } michael@0: michael@0: enum { ALLOW_MEMMOVE = true }; michael@0: michael@0: protected: michael@0: nsCOMPtr mKey; michael@0: }; michael@0: michael@0: /** michael@0: * A pending accessible tree update notifications for rendered text changes. michael@0: */ michael@0: nsTHashtable > mTextHash; michael@0: michael@0: /** michael@0: * Update the accessible tree for pending rendered text change notifications. michael@0: */ michael@0: static PLDHashOperator TextEnumerator(nsCOMPtrHashKey* aEntry, michael@0: void* aUserArg); michael@0: michael@0: /** michael@0: * Other notifications like DOM events. Don't make this an nsAutoTArray; we michael@0: * use SwapElements() on it. michael@0: */ michael@0: nsTArray > mNotifications; michael@0: }; michael@0: michael@0: } // namespace a11y michael@0: } // namespace mozilla michael@0: michael@0: #endif // mozilla_a11y_NotificationController_h_