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