author | Benjamin Smedberg <benjamin@smedbergs.us> |
Thu, 28 Oct 2010 10:05:20 -0400 | |
changeset 57222 | 3db23492203ff76d97ade9e85436b99a165d5040 |
parent 57221 | 9adc8f43d987fdf4ff0bd5945fbe9753f9c08bed (current diff) |
parent 56643 | 3369bc46ff9c2ee23fca0e4d49bf3568955f05d9 (diff) |
child 57223 | 167349934e8dbc73de2dcf7eef7174574d6b8bd0 |
push id | 16846 |
push user | bsmedberg@mozilla.com |
push date | Wed, 10 Nov 2010 15:29:47 +0000 |
treeherder | mozilla-central@bdbef533364f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 596451 |
milestone | 2.0b8pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/Makefile.in +++ b/Makefile.in @@ -63,16 +63,20 @@ tier_base_dirs = \ build \ probes \ $(NULL) ifndef LIBXUL_SDK tier_base_dirs += \ memory \ $(NULL) +ifeq ($(OS_TARGET),Android) +tier_base_dirs += other-licenses/android +endif + endif ifdef COMPILE_ENVIRONMENT include $(topsrcdir)/$(MOZ_BUILD_APP)/build.mk endif include $(topsrcdir)/config/config.mk @@ -163,17 +167,17 @@ SYM_STORE_SOURCE_DIRS := $(topsrcdir) include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk ifdef MOZ_SYMBOLS_EXTRA_BUILDID EXTRA_BUILDID := -$(MOZ_SYMBOLS_EXTRA_BUILDID) endif SYMBOL_INDEX_NAME = \ - $(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_ARCH)-$(BUILDID)$(EXTRA_BUILDID)-symbols.txt + $(MOZ_APP_NAME)-$(MOZ_APP_VERSION)-$(OS_TARGET)-$(BUILDID)$(EXTRA_BUILDID)-symbols.txt buildsymbols: ifdef MOZ_CRASHREPORTER echo building symbol store $(RM) -rf $(DIST)/crashreporter-symbols $(RM) -f "$(DIST)/$(SYMBOL_ARCHIVE_BASENAME).zip" $(NSINSTALL) -D $(DIST)/crashreporter-symbols $(PYTHON) $(topsrcdir)/toolkit/crashreporter/tools/symbolstore.py \
--- a/accessible/public/nsIAccessibilityService.h +++ b/accessible/public/nsIAccessibilityService.h @@ -104,18 +104,17 @@ public: CreateHTMLGroupboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0; virtual already_AddRefed<nsAccessible> CreateHTMLHRAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0; virtual already_AddRefed<nsAccessible> CreateHTMLImageAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0; virtual already_AddRefed<nsAccessible> CreateHTMLLabelAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0; virtual already_AddRefed<nsAccessible> - CreateHTMLLIAccessible(nsIContent* aContent, nsIPresShell* aPresShell, - const nsAString& aBulletText) = 0; + CreateHTMLLIAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0; virtual already_AddRefed<nsAccessible> CreateHTMLListboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0; virtual already_AddRefed<nsAccessible> CreateHTMLMediaAccessible(nsIContent* aContent, nsIPresShell* aPresShell) = 0; virtual already_AddRefed<nsAccessible> CreateHTMLObjectFrameAccessible(nsObjectFrame* aFrame, nsIContent* aContent, nsIPresShell* aPresShell) = 0; virtual already_AddRefed<nsAccessible> @@ -136,51 +135,49 @@ public: /** * Adds/remove ATK root accessible for gtk+ native window to/from children * of the application accessible. */ virtual nsAccessible* AddNativeRootAccessible(void* aAtkAccessible) = 0; virtual void RemoveNativeRootAccessible(nsAccessible* aRootAccessible) = 0; /** - * Used to describe sort of changes leading to accessible tree invalidation. + * Notification used to update the accessible tree when new content is + * inserted. */ - enum { - NODE_APPEND = 0x01, - NODE_REMOVE = 0x02, - NODE_SIGNIFICANT_CHANGE = 0x03, - FRAME_SHOW = 0x04, - FRAME_HIDE = 0x05, - FRAME_SIGNIFICANT_CHANGE = 0x06 - }; + virtual void ContentRangeInserted(nsIPresShell* aPresShell, + nsIContent* aContainer, + nsIContent* aStartChild, + nsIContent* aEndChild) = 0; /** - * Invalidate the accessible tree when DOM tree or frame tree is changed. - * - * @param aPresShell [in] the presShell where changes occurred - * @param aContent [in] the affected DOM content - * @param aChangeType [in] the change type (see constants declared above) + * Notification used to update the accessible tree when content is removed. */ - virtual nsresult InvalidateSubtreeFor(nsIPresShell *aPresShell, - nsIContent *aContent, - PRUint32 aChangeType) = 0; + virtual void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aContainer, + nsIContent* aChild) = 0; /** * Notify accessibility that anchor jump has been accomplished to the given * target. Used by layout. */ virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget) = 0; /** * Notify the accessibility service that the given presshell is * being destroyed. */ virtual void PresShellDestroyed(nsIPresShell *aPresShell) = 0; /** + * Recreate an accessible for the given content node in the presshell. + */ + virtual void RecreateAccessible(nsIPresShell* aPresShell, + nsIContent* aContent) = 0; + + /** * Fire accessible event of the given type for the given target. * * @param aEvent [in] accessible event type * @param aTarget [in] target of accessible event */ virtual void FireAccessibleEvent(PRUint32 aEvent, nsAccessible* aTarget) = 0; };
--- a/accessible/src/Makefile.in +++ b/accessible/src/Makefile.in @@ -70,10 +70,12 @@ DIRS += xul endif ifndef DISABLE_XFORMS_HOOKS DIRS += xforms endif include $(topsrcdir)/config/rules.mk +GARBAGE += $(LIB_PREFIX)accessibility_toolkit_s.$(LIB_SUFFIX) $(LIB_PREFIX)accessibility_toolkit_s.$(LIB_SUFFIX).fake + libs:: $(INSTALL) $(PLATFORM_DIR)/$(LIB_PREFIX)accessibility_toolkit_s.$(LIB_SUFFIX) $(wildcard $(PLATFORM_DIR)/$(LIB_PREFIX)accessibility_toolkit_s.$(LIB_SUFFIX).fake) .
--- a/accessible/src/base/AccEvent.cpp +++ b/accessible/src/base/AccEvent.cpp @@ -59,29 +59,25 @@ //////////////////////////////////////////////////////////////////////////////// // AccEvent //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // AccEvent constructors AccEvent::AccEvent(PRUint32 aEventType, nsAccessible* aAccessible, - PRBool aIsAsync, EIsFromUserInput aIsFromUserInput, - EEventRule aEventRule) : - mEventType(aEventType), mEventRule(aEventRule), mIsAsync(aIsAsync), - mAccessible(aAccessible) + EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) : + mEventType(aEventType), mEventRule(aEventRule), mAccessible(aAccessible) { CaptureIsFromUserInput(aIsFromUserInput); } AccEvent::AccEvent(PRUint32 aEventType, nsINode* aNode, - PRBool aIsAsync, EIsFromUserInput aIsFromUserInput, - EEventRule aEventRule) : - mEventType(aEventType), mEventRule(aEventRule), mIsAsync(aIsAsync), - mNode(aNode) + EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) : + mEventType(aEventType), mEventRule(aEventRule), mNode(aNode) { CaptureIsFromUserInput(aIsFromUserInput); } //////////////////////////////////////////////////////////////////////////////// // AccEvent public methods nsAccessible * @@ -213,58 +209,27 @@ AccEvent::CaptureIsFromUserInput(EIsFrom return; } mIsFromUserInput = esm->IsHandlingUserInputExternal(); } //////////////////////////////////////////////////////////////////////////////// -// AccReorderEvent -//////////////////////////////////////////////////////////////////////////////// - -AccReorderEvent:: - AccReorderEvent(nsAccessible* aAccTarget, PRBool aIsAsynch, - PRBool aIsUnconditional, nsINode* aReasonNode) : - AccEvent(::nsIAccessibleEvent::EVENT_REORDER, aAccTarget, - aIsAsynch, eAutoDetect, AccEvent::eCoalesceFromSameSubtree), - mUnconditionalEvent(aIsUnconditional), mReasonNode(aReasonNode) -{ -} - -PRBool -AccReorderEvent::IsUnconditionalEvent() -{ - return mUnconditionalEvent; -} - -PRBool -AccReorderEvent::HasAccessibleInReasonSubtree() -{ - if (!mReasonNode) - return PR_FALSE; - - nsAccessible *accessible = GetAccService()->GetAccessible(mReasonNode); - return accessible || nsAccUtils::HasAccessibleChildren(mReasonNode); -} - - -//////////////////////////////////////////////////////////////////////////////// // AccStateChangeEvent //////////////////////////////////////////////////////////////////////////////// // Note: we pass in eAllowDupes to the base class because we don't currently // support correct state change coalescence (XXX Bug 569356). Also we need to // decide how to coalesce events created via accessible (instead of node). AccStateChangeEvent:: AccStateChangeEvent(nsAccessible* aAccessible, PRUint32 aState, PRBool aIsExtraState, - PRBool aIsEnabled, PRBool aIsAsynch, - EIsFromUserInput aIsFromUserInput): - AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible, aIsAsynch, + PRBool aIsEnabled, EIsFromUserInput aIsFromUserInput): + AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible, aIsFromUserInput, eAllowDupes), mState(aState), mIsExtraState(aIsExtraState), mIsEnabled(aIsEnabled) { } AccStateChangeEvent:: AccStateChangeEvent(nsINode* aNode, PRUint32 aState, PRBool aIsExtraState, PRBool aIsEnabled): @@ -310,67 +275,92 @@ AccStateChangeEvent::CreateXPCOMObject() // node. This means we won't try to create an accessible based on the node when // we are ready to fire the event and so we will no longer assert at that point // if the node was removed from the document. Either way, the AT won't work with // a defunct accessible so the behaviour should be equivalent. // XXX revisit this when coalescence is faster (eCoalesceFromSameSubtree) AccTextChangeEvent:: AccTextChangeEvent(nsAccessible* aAccessible, PRInt32 aStart, nsAString& aModifiedText, PRBool aIsInserted, - PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput) + EIsFromUserInput aIsFromUserInput) : AccEvent(aIsInserted ? static_cast<PRUint32>(nsIAccessibleEvent::EVENT_TEXT_INSERTED) : static_cast<PRUint32>(nsIAccessibleEvent::EVENT_TEXT_REMOVED), - aAccessible, aIsAsynch, aIsFromUserInput, eAllowDupes) + aAccessible, aIsFromUserInput, eAllowDupes) , mStart(aStart) , mIsInserted(aIsInserted) , mModifiedText(aModifiedText) { } already_AddRefed<nsAccEvent> AccTextChangeEvent::CreateXPCOMObject() { nsAccEvent* event = new nsAccTextChangeEvent(this); NS_IF_ADDREF(event); return event; } //////////////////////////////////////////////////////////////////////////////// +// AccMutationEvent +//////////////////////////////////////////////////////////////////////////////// + +AccMutationEvent:: + AccMutationEvent(PRUint32 aEventType, nsAccessible* aTarget, + nsINode* aTargetNode, EIsFromUserInput aIsFromUserInput) : + AccEvent(aEventType, aTarget, aIsFromUserInput, eCoalesceFromSameSubtree) +{ + mNode = aTargetNode; +} + + +//////////////////////////////////////////////////////////////////////////////// // AccHideEvent //////////////////////////////////////////////////////////////////////////////// AccHideEvent:: AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode, - PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput) : - AccEvent(nsIAccessibleEvent::EVENT_HIDE, aTarget, aIsAsynch, - aIsFromUserInput, eCoalesceFromSameSubtree) + EIsFromUserInput aIsFromUserInput) : + AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode, + aIsFromUserInput) { - mNode = aTargetNode; mParent = mAccessible->GetCachedParent(); mNextSibling = mAccessible->GetCachedNextSibling(); mPrevSibling = mAccessible->GetCachedPrevSibling(); } //////////////////////////////////////////////////////////////////////////////// +// AccShowEvent +//////////////////////////////////////////////////////////////////////////////// + +AccShowEvent:: + AccShowEvent(nsAccessible* aTarget, nsINode* aTargetNode, + EIsFromUserInput aIsFromUserInput) : + AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode, + aIsFromUserInput) +{ +} + + +//////////////////////////////////////////////////////////////////////////////// // AccCaretMoveEvent //////////////////////////////////////////////////////////////////////////////// AccCaretMoveEvent:: AccCaretMoveEvent(nsAccessible* aAccessible, PRInt32 aCaretOffset) : - AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible, PR_TRUE), // Currently always asynch + AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible), mCaretOffset(aCaretOffset) { } AccCaretMoveEvent:: AccCaretMoveEvent(nsINode* aNode) : - AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aNode, PR_TRUE), // Currently always asynch + AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aNode), mCaretOffset(-1) { } already_AddRefed<nsAccEvent> AccCaretMoveEvent::CreateXPCOMObject() { nsAccEvent* event = new nsAccCaretMoveEvent(this); @@ -380,19 +370,18 @@ AccCaretMoveEvent::CreateXPCOMObject() //////////////////////////////////////////////////////////////////////////////// // AccTableChangeEvent //////////////////////////////////////////////////////////////////////////////// AccTableChangeEvent:: AccTableChangeEvent(nsAccessible* aAccessible, PRUint32 aEventType, - PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols, - PRBool aIsAsynch) : - AccEvent(aEventType, aAccessible, aIsAsynch), + PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols) : + AccEvent(aEventType, aAccessible), mRowOrColIndex(aRowOrColIndex), mNumRowsOrCols(aNumRowsOrCols) { } already_AddRefed<nsAccEvent> AccTableChangeEvent::CreateXPCOMObject() { nsAccEvent* event = new nsAccTableChangeEvent(this);
--- a/accessible/src/base/AccEvent.h +++ b/accessible/src/base/AccEvent.h @@ -36,16 +36,18 @@ * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #ifndef _AccEvent_H_ #define _AccEvent_H_ +#include "nsIAccessibleEvent.h" + #include "nsAccessible.h" class nsAccEvent; class nsDocAccessible; // Constants used to point whether the event is from user input. enum EIsFromUserInput { @@ -85,49 +87,48 @@ public: eRemoveDupes, // eDoNotEmit : This event is confirmed as a duplicate, do not emit it. eDoNotEmit }; // Initialize with an nsIAccessible AccEvent(PRUint32 aEventType, nsAccessible* aAccessible, - PRBool aIsAsynch = PR_FALSE, EIsFromUserInput aIsFromUserInput = eAutoDetect, EEventRule aEventRule = eRemoveDupes); // Initialize with an nsIDOMNode - AccEvent(PRUint32 aEventType, nsINode* aNode, PRBool aIsAsynch = PR_FALSE, + AccEvent(PRUint32 aEventType, nsINode* aNode, EIsFromUserInput aIsFromUserInput = eAutoDetect, EEventRule aEventRule = eRemoveDupes); virtual ~AccEvent() {} // AccEvent PRUint32 GetEventType() const { return mEventType; } EEventRule GetEventRule() const { return mEventRule; } - PRBool IsAsync() const { return mIsAsync; } PRBool IsFromUserInput() const { return mIsFromUserInput; } nsAccessible *GetAccessible(); nsDocAccessible* GetDocAccessible(); nsINode* GetNode(); /** * Create and return an XPCOM object for accessible event object. */ virtual already_AddRefed<nsAccEvent> CreateXPCOMObject(); /** * Down casting. */ enum EventGroup { eGenericEvent, - eReorderEvent, eStateChangeEvent, eTextChangeEvent, + eMutationEvent, eHideEvent, + eShowEvent, eCaretMoveEvent, eTableChangeEvent }; static const EventGroup kEventGroup = eGenericEvent; virtual unsigned int GetEventGroups() const { return 1U << eGenericEvent; @@ -149,66 +150,32 @@ protected: * Determine whether the event is from user input by event state manager if * it's not pointed explicetly. */ void CaptureIsFromUserInput(EIsFromUserInput aIsFromUserInput); PRBool mIsFromUserInput; PRUint32 mEventType; EEventRule mEventRule; - PRPackedBool mIsAsync; nsRefPtr<nsAccessible> mAccessible; nsCOMPtr<nsINode> mNode; friend class nsAccEventQueue; }; /** - * Accessible reorder event. - */ -class AccReorderEvent : public AccEvent -{ -public: - AccReorderEvent(nsAccessible* aAccTarget, PRBool aIsAsynch, - PRBool aIsUnconditional, nsINode* aReasonNode); - - // AccEvent - static const EventGroup kEventGroup = eReorderEvent; - virtual unsigned int GetEventGroups() const - { - return AccEvent::GetEventGroups() | (1U << eReorderEvent); - } - - // AccReorderEvent - /** - * Return true if event is unconditional, i.e. must be fired. - */ - PRBool IsUnconditionalEvent(); - - /** - * Return true if changed DOM node has accessible in its tree. - */ - PRBool HasAccessibleInReasonSubtree(); - -private: - PRBool mUnconditionalEvent; - nsCOMPtr<nsINode> mReasonNode; -}; - - -/** * Accessible state change event. */ class AccStateChangeEvent: public AccEvent { public: AccStateChangeEvent(nsAccessible* aAccessible, PRUint32 aState, PRBool aIsExtraState, - PRBool aIsEnabled, PRBool aIsAsynch = PR_FALSE, + PRBool aIsEnabled, EIsFromUserInput aIsFromUserInput = eAutoDetect); AccStateChangeEvent(nsINode* aNode, PRUint32 aState, PRBool aIsExtraState, PRBool aIsEnabled); AccStateChangeEvent(nsINode* aNode, PRUint32 aState, PRBool aIsExtraState); // AccEvent @@ -234,18 +201,17 @@ private: /** * Accessible text change event. */ class AccTextChangeEvent: public AccEvent { public: AccTextChangeEvent(nsAccessible* aAccessible, PRInt32 aStart, - nsAString& aModifiedText, - PRBool aIsInserted, PRBool aIsAsynch = PR_FALSE, + nsAString& aModifiedText, PRBool aIsInserted, EIsFromUserInput aIsFromUserInput = eAutoDetect); // AccEvent virtual already_AddRefed<nsAccEvent> CreateXPCOMObject(); static const EventGroup kEventGroup = eTextChangeEvent; virtual unsigned int GetEventGroups() const { @@ -264,42 +230,86 @@ private: PRBool mIsInserted; nsString mModifiedText; friend class nsAccEventQueue; }; /** - * Accessible hide events. + * Base class for show and hide accessible events. */ -class AccHideEvent : public AccEvent +class AccMutationEvent: public AccEvent +{ +public: + AccMutationEvent(PRUint32 aEventType, nsAccessible* aTarget, + nsINode* aTargetNode, EIsFromUserInput aIsFromUserInput); + + // Event + static const EventGroup kEventGroup = eMutationEvent; + virtual unsigned int GetEventGroups() const + { + return AccEvent::GetEventGroups() | (1U << eMutationEvent); + } + + // MutationEvent + bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; } + bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; } + +protected: + nsRefPtr<AccTextChangeEvent> mTextChangeEvent; + + friend class nsAccEventQueue; +}; + + +/** + * Accessible hide event. + */ +class AccHideEvent: public AccMutationEvent { public: AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode, - PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput); + EIsFromUserInput aIsFromUserInput); // Event static const EventGroup kEventGroup = eHideEvent; virtual unsigned int GetEventGroups() const { - return AccEvent::GetEventGroups() | (1U << eHideEvent); + return AccMutationEvent::GetEventGroups() | (1U << eHideEvent); } protected: nsRefPtr<nsAccessible> mParent; nsRefPtr<nsAccessible> mNextSibling; nsRefPtr<nsAccessible> mPrevSibling; - nsRefPtr<AccTextChangeEvent> mTextChangeEvent; friend class nsAccEventQueue; }; /** + * Accessible show event. + */ +class AccShowEvent: public AccMutationEvent +{ +public: + AccShowEvent(nsAccessible* aTarget, nsINode* aTargetNode, + EIsFromUserInput aIsFromUserInput); + + // Event + static const EventGroup kEventGroup = eShowEvent; + virtual unsigned int GetEventGroups() const + { + return AccMutationEvent::GetEventGroups() | (1U << eShowEvent); + } +}; + + +/** * Accessible caret move event. */ class AccCaretMoveEvent: public AccEvent { public: AccCaretMoveEvent(nsAccessible* aAccessible, PRInt32 aCaretOffset); AccCaretMoveEvent(nsINode* aNode); @@ -322,18 +332,17 @@ private: /** * Accessible table change event. */ class AccTableChangeEvent : public AccEvent { public: AccTableChangeEvent(nsAccessible* aAccessible, PRUint32 aEventType, - PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols, - PRBool aIsAsynch); + PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols); // AccEvent virtual already_AddRefed<nsAccEvent> CreateXPCOMObject(); static const EventGroup kEventGroup = eTableChangeEvent; virtual unsigned int GetEventGroups() const { return AccEvent::GetEventGroups() | (1U << eTableChangeEvent);
--- a/accessible/src/base/nsAccDocManager.cpp +++ b/accessible/src/base/nsAccDocManager.cpp @@ -67,44 +67,35 @@ nsDocAccessible* nsAccDocManager::GetDocAccessible(nsIDocument *aDocument) { if (!aDocument) return nsnull; // Ensure CacheChildren is called before we query cache. nsAccessNode::GetApplicationAccessible()->EnsureChildren(); - nsDocAccessible *docAcc = - mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument)); + nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument); if (docAcc) return docAcc; return CreateDocOrRootAccessible(aDocument); } nsAccessible* -nsAccDocManager::FindAccessibleInCache(void *aUniqueID) const +nsAccDocManager::FindAccessibleInCache(nsINode* aNode) const { nsSearchAccessibleInCacheArg arg; - arg.mUniqueID = aUniqueID; + arg.mNode = aNode; mDocAccessibleCache.EnumerateRead(SearchAccessibleInDocCache, static_cast<void*>(&arg)); return arg.mAccessible; } -void -nsAccDocManager::ShutdownDocAccessiblesInTree(nsIDocument *aDocument) -{ - nsCOMPtr<nsISupports> container = aDocument->GetContainer(); - nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container); - ShutdownDocAccessiblesInTree(treeItem, aDocument); -} - //////////////////////////////////////////////////////////////////////////////// // nsAccDocManager protected PRBool nsAccDocManager::Init() { mDocAccessibleCache.Init(4); @@ -128,32 +119,16 @@ nsAccDocManager::Shutdown() do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID); if (progress) progress->RemoveProgressListener(static_cast<nsIWebProgressListener*>(this)); ClearDocCache(); } -void -nsAccDocManager::ShutdownDocAccessible(nsIDocument *aDocument) -{ - nsDocAccessible* docAccessible = - mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument)); - if (!docAccessible) - return; - - // We're allowed to not remove listeners when accessible document is shutdown - // since we don't keep strong reference on chrome event target and listeners - // are removed automatically when chrome event target goes away. - - docAccessible->Shutdown(); - mDocAccessibleCache.Remove(static_cast<void*>(aDocument)); -} - //////////////////////////////////////////////////////////////////////////////// // nsISupports NS_IMPL_THREADSAFE_ISUPPORTS3(nsAccDocManager, nsIWebProgressListener, nsIDOMEventListener, nsISupportsWeakReference) @@ -210,18 +185,17 @@ nsAccDocManager::OnStateChange(nsIWebPro // Document loading was started. NS_LOG_ACCDOCLOAD("start document loading", aWebProgress, aRequest, aStateFlags) if (!IsEventTargetDocument(document)) return NS_OK; - nsDocAccessible *docAcc = - mDocAccessibleCache.GetWeak(static_cast<void*>(document)); + nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(document); if (!docAcc) return NS_OK; nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow)); nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav)); NS_ENSURE_STATE(docShell); // Fire reload and state busy events on existing document accessible while @@ -314,17 +288,24 @@ nsAccDocManager::HandleEvent(nsIDOMEvent NS_LOG_ACCDOCDESTROY("received 'pagehide' event", document) // Ignore 'pagehide' on temporary documents since we ignore them entirely in // accessibility. if (document->IsInitialDocument()) return NS_OK; // Shutdown this one and sub document accessibles. - ShutdownDocAccessiblesInTree(document); + + // We're allowed to not remove listeners when accessible document is + // shutdown since we don't keep strong reference on chrome event target and + // listeners are removed automatically when chrome event target goes away. + nsDocAccessible* docAccessible = mDocAccessibleCache.GetWeak(document); + if (docAccessible) + docAccessible->Shutdown(); + return NS_OK; } // XXX: handle error pages loading separately since they get neither // webprogress notifications nor 'pageshow' event. if (type.EqualsLiteral("DOMContentLoaded") && nsCoreUtils::IsErrorPage(document)) { NS_LOG_ACCDOCLOAD2("handled 'DOMContentLoaded' event", document) @@ -341,45 +322,35 @@ nsAccDocManager::HandleEvent(nsIDOMEvent void nsAccDocManager::HandleDOMDocumentLoad(nsIDocument *aDocument, PRUint32 aLoadEventType, PRBool aMarkAsLoaded) { // Document accessible can be created before we were notified the DOM document // was loaded completely. However if it's not created yet then create it. - nsDocAccessible *docAcc = - mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument)); - + nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument); if (!docAcc) { docAcc = CreateDocOrRootAccessible(aDocument); NS_ASSERTION(docAcc, "Can't create document accessible!"); if (!docAcc) return; } if (aMarkAsLoaded) docAcc->MarkAsLoaded(); // Do not fire document complete/stop events for root chrome document // accessibles and for frame/iframe documents because // a) screen readers start working on focus event in the case of root chrome // documents // b) document load event on sub documents causes screen readers to act is if // entire page is reloaded. - if (!IsEventTargetDocument(aDocument)) { - // XXX: AT doesn't update their virtual buffer once frame is loaded and it - // has dynamic content added after frame load. There's something wrong how - // we handle this changes. - if (!nsCoreUtils::IsRootDocument(aDocument)) { - docAcc->InvalidateCacheSubtree(nsnull, - nsIAccessibilityService::NODE_SIGNIFICANT_CHANGE); - } + if (!IsEventTargetDocument(aDocument)) return; - } // Fire complete/load stopped if the load event type is given. if (aLoadEventType) { nsRefPtr<AccEvent> loadEvent = new AccEvent(aLoadEventType, aDocument); docAcc->FireDelayedAccessibleEvent(loadEvent); } // Fire busy state change event. @@ -432,19 +403,20 @@ nsAccDocManager::AddListeners(nsIDocumen NS_EVENT_FLAG_CAPTURE, nsnull); NS_LOG_ACCDOCCREATE_TEXT(" added 'DOMContentLoaded' listener") } } nsDocAccessible* nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument) { - // Ignore temporary, hiding and svg resource documents. + // Ignore temporary, hiding, resource documents and documents without + // docshell. if (aDocument->IsInitialDocument() || !aDocument->IsVisible() || - aDocument->GetDisplayDocument()) + aDocument->IsResourceDoc() || !aDocument->IsActive()) return nsnull; // Ignore documents without presshell. nsIPresShell *presShell = aDocument->GetShell(); if (!presShell) return nsnull; // Do not create document accessible until role content is loaded, otherwise @@ -487,92 +459,63 @@ nsAccDocManager::CreateDocOrRootAccessib nsDocAccessible *docAcc = isRootDoc ? new nsRootAccessibleWrap(aDocument, rootElm, weakShell) : new nsDocAccessibleWrap(aDocument, rootElm, weakShell); if (!docAcc) return nsnull; // Cache and addref document accessible. - if (!mDocAccessibleCache.Put(static_cast<void*>(aDocument), docAcc)) { + if (!mDocAccessibleCache.Put(aDocument, docAcc)) { delete docAcc; return nsnull; } // XXX: ideally we should initialize an accessible and then put it into tree, // we can't since document accessible fires reorder event on its container // while initialized. if (!outerDocAcc->AppendChild(docAcc) || !GetAccService()->InitAccessible(docAcc, nsAccUtils::GetRoleMapEntry(aDocument))) { - mDocAccessibleCache.Remove(static_cast<void*>(aDocument)); + mDocAccessibleCache.Remove(aDocument); return nsnull; } NS_LOG_ACCDOCCREATE("document creation finished", aDocument) AddListeners(aDocument, isRootDoc); return docAcc; } -void -nsAccDocManager::ShutdownDocAccessiblesInTree(nsIDocShellTreeItem *aTreeItem, - nsIDocument *aDocument) -{ - nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(aTreeItem)); - - if (treeNode) { - PRInt32 subDocumentsCount = 0; - treeNode->GetChildCount(&subDocumentsCount); - for (PRInt32 idx = 0; idx < subDocumentsCount; idx++) { - nsCOMPtr<nsIDocShellTreeItem> treeItemChild; - treeNode->GetChildAt(idx, getter_AddRefs(treeItemChild)); - NS_ASSERTION(treeItemChild, "No tree item when there should be"); - if (!treeItemChild) - continue; - - nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(treeItemChild)); - nsCOMPtr<nsIContentViewer> contentViewer; - docShell->GetContentViewer(getter_AddRefs(contentViewer)); - if (!contentViewer) - continue; - - ShutdownDocAccessiblesInTree(treeItemChild, contentViewer->GetDocument()); - } - } - - ShutdownDocAccessible(aDocument); -} - //////////////////////////////////////////////////////////////////////////////// // nsAccDocManager static PLDHashOperator -nsAccDocManager::ClearDocCacheEntry(const void* aKey, +nsAccDocManager::ClearDocCacheEntry(const nsIDocument* aKey, nsRefPtr<nsDocAccessible>& aDocAccessible, void* aUserArg) { NS_ASSERTION(aDocAccessible, "Calling ClearDocCacheEntry with a NULL pointer!"); if (aDocAccessible) aDocAccessible->Shutdown(); return PL_DHASH_REMOVE; } PLDHashOperator -nsAccDocManager::SearchAccessibleInDocCache(const void* aKey, +nsAccDocManager::SearchAccessibleInDocCache(const nsIDocument* aKey, nsDocAccessible* aDocAccessible, void* aUserArg) { NS_ASSERTION(aDocAccessible, "No doc accessible for the object in doc accessible cache!"); if (aDocAccessible) { nsSearchAccessibleInCacheArg* arg = static_cast<nsSearchAccessibleInCacheArg*>(aUserArg); - arg->mAccessible = aDocAccessible->GetCachedAccessible(arg->mUniqueID); + arg->mAccessible = aDocAccessible->GetCachedAccessible(arg->mNode); if (arg->mAccessible) return PL_DHASH_STOP; } return PL_DHASH_NEXT; }
--- a/accessible/src/base/nsAccDocManager.h +++ b/accessible/src/base/nsAccDocManager.h @@ -68,51 +68,47 @@ public: * Return document accessible for the given DOM node. */ nsDocAccessible *GetDocAccessible(nsIDocument *aDocument); /** * Search through all document accessibles for an accessible with the given * unique id. */ - nsAccessible *FindAccessibleInCache(void *aUniqueID) const; - - /** - * Shutdown document accessibles in the tree starting from the given one. - * - * @param aDocument [in] the DOM document of start document accessible - */ - void ShutdownDocAccessiblesInTree(nsIDocument *aDocument); + nsAccessible* FindAccessibleInCache(nsINode* aNode) const; /** * Return document accessible from the cache. Convenient method for testing. */ inline nsDocAccessible* GetDocAccessibleFromCache(nsIDocument* aDocument) const { - return mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument)); + return mDocAccessibleCache.GetWeak(aDocument); + } + + /** + * Called by document accessible when it gets shutdown. + */ + inline void NotifyOfDocumentShutdown(nsIDocument* aDocument) + { + mDocAccessibleCache.Remove(aDocument); } protected: nsAccDocManager() { }; /** * Initialize the manager. */ PRBool Init(); /** * Shutdown the manager. */ void Shutdown(); - /** - * Shutdown the document accessible. - */ - void ShutdownDocAccessible(nsIDocument* aDocument); - private: nsAccDocManager(const nsAccDocManager&); nsAccDocManager& operator =(const nsAccDocManager&); private: /** * Create an accessible document if it was't created and fire accessibility * events if needed. @@ -151,49 +147,43 @@ private: */ void AddListeners(nsIDocument *aDocument, PRBool aAddPageShowListener); /** * Create document or root accessible. */ nsDocAccessible *CreateDocOrRootAccessible(nsIDocument *aDocument); - /** - * Shutdown document accessibles in the tree starting from given tree item. - */ - void ShutdownDocAccessiblesInTree(nsIDocShellTreeItem *aTreeItem, - nsIDocument *aDocument); - - typedef nsRefPtrHashtable<nsVoidPtrHashKey, nsDocAccessible> + typedef nsRefPtrHashtable<nsPtrHashKey<const nsIDocument>, nsDocAccessible> nsDocAccessibleHashtable; /** * Shutdown and remove the document accessible from cache. */ static PLDHashOperator - ClearDocCacheEntry(const void* aKey, + ClearDocCacheEntry(const nsIDocument* aKey, nsRefPtr<nsDocAccessible>& aDocAccessible, void* aUserArg); /** * Clear the cache and shutdown the document accessibles. */ void ClearDocCache() { mDocAccessibleCache.Enumerate(ClearDocCacheEntry, static_cast<void*>(this)); } struct nsSearchAccessibleInCacheArg { nsAccessible *mAccessible; - void *mUniqueID; + nsINode* mNode; }; static PLDHashOperator - SearchAccessibleInDocCache(const void* aKey, + SearchAccessibleInDocCache(const nsIDocument* aKey, nsDocAccessible* aDocAccessible, void* aUserArg); nsDocAccessibleHashtable mDocAccessibleCache; }; /** * nsAccDocManager debugging macros.
--- a/accessible/src/base/nsAccTreeWalker.cpp +++ b/accessible/src/base/nsAccTreeWalker.cpp @@ -69,19 +69,21 @@ nsAccTreeWalker:: PRBool aWalkAnonContent) : mWeakShell(aShell), mState(nsnull) { NS_ASSERTION(aContent, "No node for the accessible tree walker!"); if (aContent) mState = new WalkState(aContent); - mChildType = aWalkAnonContent ? nsIContent::eAllChildren : + mChildFilter = aWalkAnonContent ? nsIContent::eAllChildren : nsIContent::eAllButXBL; + mChildFilter |= nsIContent::eSkipPlaceholderContent; + MOZ_COUNT_CTOR(nsAccTreeWalker); } nsAccTreeWalker::~nsAccTreeWalker() { // Clear state stack from memory while (mState) PopState(); @@ -94,17 +96,17 @@ nsAccTreeWalker::~nsAccTreeWalker() already_AddRefed<nsAccessible> nsAccTreeWalker::GetNextChildInternal(PRBool aNoWalkUp) { if (!mState || !mState->content) return nsnull; if (!mState->childList) - mState->childList = mState->content->GetChildren(mChildType); + mState->childList = mState->content->GetChildren(mChildFilter); nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell)); PRUint32 length = 0; if (mState->childList) mState->childList->GetLength(&length); while (mState->childIdx < length) {
--- a/accessible/src/base/nsAccTreeWalker.h +++ b/accessible/src/base/nsAccTreeWalker.h @@ -86,13 +86,13 @@ private: PRBool PushState(nsIContent *aNode); /** * Pop state from stack. */ void PopState(); nsCOMPtr<nsIWeakReference> mWeakShell; - PRInt32 mChildType; + PRInt32 mChildFilter; WalkState* mState; }; #endif
--- a/accessible/src/base/nsAccessNode.cpp +++ b/accessible/src/base/nsAccessNode.cpp @@ -147,19 +147,22 @@ nsAccessNode::Init() void nsAccessNode::Shutdown() { mContent = nsnull; mWeakShell = nsnull; } // nsIAccessNode -NS_IMETHODIMP nsAccessNode::GetUniqueID(void **aUniqueID) +NS_IMETHODIMP +nsAccessNode::GetUniqueID(void **aUniqueID) { - *aUniqueID = static_cast<void*>(GetNode()); + NS_ENSURE_ARG_POINTER(aUniqueID); + + *aUniqueID = UniqueID(); return NS_OK; } // nsIAccessNode NS_IMETHODIMP nsAccessNode::GetOwnerWindow(void **aWindow) { NS_ENSURE_ARG_POINTER(aWindow); @@ -305,16 +308,22 @@ already_AddRefed<nsRootAccessible> nsAcc } nsIFrame* nsAccessNode::GetFrame() { return mContent ? mContent->GetPrimaryFrame() : nsnull; } +bool +nsAccessNode::IsPrimaryForNode() const +{ + return true; +} + //////////////////////////////////////////////////////////////////////////////// // nsIAccessNode NS_IMETHODIMP nsAccessNode::GetDOMNode(nsIDOMNode **aDOMNode) { NS_ENSURE_ARG_POINTER(aDOMNode); *aDOMNode = nsnull;
--- a/accessible/src/base/nsAccessNode.h +++ b/accessible/src/base/nsAccessNode.h @@ -176,16 +176,30 @@ public: */ already_AddRefed<nsIPresShell> GetPresShell(); /** * Return presentation shell for the accessible. */ nsIWeakReference* GetWeakShell() const { return mWeakShell; } + /** + * Return the unique identifier of the accessible. + */ + void* UniqueID() { return static_cast<void*>(this); } + + /** + * Return true if the accessible is primary accessible for the given DOM node. + * + * Accessible hierarchy may be complex for single DOM node, in this case + * these accessibles share the same DOM node. The primary accessible "owns" + * that DOM node in terms it gets stored in the accessible to node map. + */ + virtual bool IsPrimaryForNode() const; + protected: nsPresContext* GetPresContext(); void LastRelease(); nsCOMPtr<nsIContent> mContent; nsCOMPtr<nsIWeakReference> mWeakShell;
--- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -69,16 +69,17 @@ #include "nsIObserverService.h" #include "nsIPluginInstance.h" #include "nsISupportsUtils.h" #include "nsObjectFrame.h" #include "nsOuterDocAccessible.h" #include "nsRootAccessibleWrap.h" #include "nsTextFragment.h" #include "mozilla/Services.h" +#include "nsIEventStateManager.h" #ifdef MOZ_XUL #include "nsXULAlertAccessible.h" #include "nsXULColorPickerAccessible.h" #include "nsXULComboboxAccessible.h" #include "nsXULFormControlAccessible.h" #include "nsXULListboxAccessibleWrap.h" #include "nsXULMenuAccessibleWrap.h" @@ -227,22 +228,20 @@ nsAccessibilityService::CreateHTMLButton nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(aPresShell)); nsAccessible* accessible = new nsHTMLButtonAccessible(aContent, weakShell); NS_IF_ADDREF(accessible); return accessible; } already_AddRefed<nsAccessible> nsAccessibilityService::CreateHTMLLIAccessible(nsIContent* aContent, - nsIPresShell* aPresShell, - const nsAString& aBulletText) + nsIPresShell* aPresShell) { nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(aPresShell)); - nsAccessible* accessible = new nsHTMLLIAccessible(aContent, weakShell, - aBulletText); + nsAccessible* accessible = new nsHTMLLIAccessible(aContent, weakShell); NS_IF_ADDREF(accessible); return accessible; } already_AddRefed<nsAccessible> nsAccessibilityService::CreateHyperTextAccessible(nsIContent* aContent, nsIPresShell* aPresShell) { @@ -468,42 +467,109 @@ nsAccessibilityService::CreateHTMLCaptio { nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(aPresShell)); nsAccessible* accessible = new nsHTMLCaptionAccessible(aContent, weakShell); NS_IF_ADDREF(accessible); return accessible; } void +nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell, + nsIContent* aContainer, + nsIContent* aStartChild, + nsIContent* aEndChild) +{ +#ifdef DEBUG_A11Y + nsAutoString tag; + aStartChild->Tag()->ToString(tag); + nsIAtom* id = aStartChild->GetID(); + nsCAutoString strid; + if (id) + id->ToUTF8String(strid); + nsAutoString ctag; + aContainer->Tag()->ToString(ctag); + nsIAtom* cid = aContainer->GetID(); + nsCAutoString strcid; + if (cid) + cid->ToUTF8String(strcid); + printf("\ncontent inserted: %s@id='%s', container: %s@id='%s', end node: %p\n\n", + NS_ConvertUTF16toUTF8(tag).get(), strid.get(), + NS_ConvertUTF16toUTF8(ctag).get(), strcid.get(), aEndChild); +#endif + + // XXX: bug 606082. aContainer is null when root element is inserted into + // document, we need to handle this and update the tree, also we need to + // update a content node of the document accessible. + if (aContainer) { + nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument()); + if (docAccessible) + docAccessible->UpdateTree(aContainer, aStartChild, aEndChild, PR_TRUE); + } +} + +void +nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell, + nsIContent* aContainer, + nsIContent* aChild) +{ +#ifdef DEBUG_A11Y + nsAutoString id; + aChild->Tag()->ToString(id); + printf("\ncontent removed: %s\n", NS_ConvertUTF16toUTF8(id).get()); +#endif + + // XXX: bug 606082. aContainer is null when root element is inserted into + // document, we need to handle this and update the tree, perhaps destroy + // the document accessible. + if (aContainer) { + nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument()); + if (docAccessible) + docAccessible->UpdateTree(aContainer, aChild, aChild->GetNextSibling(), + PR_FALSE); + } +} + +void nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell) { // Presshell destruction will automatically destroy shells for descendant // documents, so no need to worry about those. Just shut down the accessible // for this one document. That keeps us from having bad behavior in case of // deep bushy subtrees. // When document subtree containing iframe is hidden then we don't get // pagehide event for the iframe's underlying document and its presshell is // destroyed before we're notified styles were changed. Shutdown the document // accessible early. nsIDocument* doc = aPresShell->GetDocument(); if (!doc) return; NS_LOG_ACCDOCDESTROY("presshell destroyed", doc) - ShutdownDocAccessible(doc); + + nsDocAccessible* docAccessible = GetDocAccessibleFromCache(doc); + if (docAccessible) + docAccessible->Shutdown(); +} + +void +nsAccessibilityService::RecreateAccessible(nsIPresShell* aPresShell, + nsIContent* aContent) +{ + nsDocAccessible* document = GetDocAccessible(aPresShell->GetDocument()); + if (document) + document->RecreateAccessible(aContent); } // nsAccessibilityService protected nsAccessible * nsAccessibilityService::GetCachedAccessible(nsINode *aNode, nsIWeakReference *aWeakShell) { nsDocAccessible *docAccessible = GetDocAccessible(aNode->GetOwnerDoc()); - return docAccessible ? - docAccessible->GetCachedAccessible(static_cast<void*>(aNode)) : nsnull; + return docAccessible ? docAccessible->GetCachedAccessible(aNode) : nsnull; } //////////////////////////////////////////////////////////////////////////////// // nsIAccessibleRetrieval NS_IMETHODIMP nsAccessibilityService::GetApplicationAccessible(nsIAccessible **aAccessibleApplication) { @@ -691,17 +757,17 @@ nsAccessibilityService::GetAccessibleFro // Search for an accessible in each of our per document accessible object // caches. If we don't find it, and the given node is itself a document, check // our cache of document accessibles (document cache). Note usually shutdown // document accessibles are not stored in the document cache, however an // "unofficially" shutdown document (i.e. not from nsAccDocManager) can still // exist in the document cache. nsCOMPtr<nsINode> node(do_QueryInterface(aNode)); - nsAccessible* accessible = FindAccessibleInCache(static_cast<void*>(node)); + nsAccessible* accessible = FindAccessibleInCache(node); if (!accessible) { nsCOMPtr<nsIDocument> document(do_QueryInterface(node)); if (document) accessible = GetDocAccessibleFromCache(document); } NS_IF_ADDREF(*aAccessible = accessible); return NS_OK; @@ -729,35 +795,35 @@ nsAccessibilityService::GetAccessible(ns nsCOMPtr<nsIWeakReference> weakShell(nsCoreUtils::GetWeakShellFor(aNode)); if (weakShell) return GetAccessibleByRule(aNode, weakShell, eGetAccForNode); } return nsnull; } nsAccessible* -nsAccessibilityService::GetCachedContainerAccessible(nsINode* aNode) +nsAccessibilityService::GetCachedAccessibleOrContainer(nsINode* aNode) { if (!aNode) return nsnull; nsIDocument *document = aNode->GetCurrentDoc(); if (!document) return nsnull; nsIPresShell *presShell = document->GetShell(); if (!presShell) return nsnull; nsINode *currNode = aNode; nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(presShell)); nsAccessible *accessible = nsnull; - while ((currNode = currNode->GetNodeParent()) && - !(accessible = GetCachedAccessible(currNode, weakShell))); + while (!(accessible = GetCachedAccessible(currNode, weakShell)) && + (currNode = currNode->GetNodeParent())); return accessible; } PRBool nsAccessibilityService::InitAccessible(nsAccessible *aAccessible, nsRoleMapEntry *aRoleMapEntry) { @@ -854,18 +920,17 @@ nsAccessibilityService::GetOrCreateAcces // Frames can be deallocated when we flush layout, or when we call into code // that can flush layout, either directly, or via DOM manipulation, or some // CSS styles like :hover. We use the weak frame checks to avoid calling // methods on a dead frame pointer. nsWeakFrame weakFrame = content->GetPrimaryFrame(); // Check frame to see if it is hidden. - if (!weakFrame.GetFrame() || - !weakFrame.GetFrame()->GetStyleVisibility()->IsVisible()) { + if (!weakFrame.GetFrame()) { if (aIsHidden) *aIsHidden = PR_TRUE; return nsnull; } if (weakFrame.GetFrame()->GetContent() != content) { // Not the main content for this frame. This happens because <area> @@ -1192,33 +1257,34 @@ nsAccessibilityService::HasUniversalAria nsAccessible* nsAccessibilityService::GetAccessibleByRule(nsINode* aNode, nsIWeakReference* aWeakShell, EWhatAccToGet aWhatToGet) { if (!aNode || !aWeakShell) return nsnull; - nsAccessible* cachedAcc = GetCachedAccessible(aNode, aWeakShell); - if (cachedAcc) { - if (aWhatToGet & eGetAccForNode) + if (aWhatToGet & eGetAccForNode) { + nsAccessible* cachedAcc = GetCachedAccessible(aNode, aWeakShell); + if (cachedAcc && cachedAcc->IsBoundToParent()) return cachedAcc; - - // XXX: while nsAccessible::GetParent() tries to repair broken tree and - // may not return cached parent then we use GetAccessibleOrContainer(). - return GetAccessibleByRule(aNode->GetNodeParent(), aWeakShell, - eGetAccForNodeOrContainer); } - // Go up looking for the nearest accessible container stored in cache. + // Go up looking for the nearest accessible container having cached children. nsTArray<nsINode*> nodes; + nsINode* node = aNode; - while ((node = node->GetNodeParent()) && - !(cachedAcc = GetCachedAccessible(node, aWeakShell))) + nsAccessible* cachedAcc = nsnull; + while ((node = node->GetNodeParent())) { + cachedAcc = GetCachedAccessible(node, aWeakShell); + if (cachedAcc && cachedAcc->IsBoundToParent()) + break; + nodes.AppendElement(node); + } // Node is not in accessible document. if (!cachedAcc) return nsnull; // If children of the cached accessible weren't initialized then go down to // the given node and create accessible tree. nsAccessible* containerAcc = cachedAcc; @@ -1629,18 +1695,17 @@ nsAccessibilityService::CreateHTMLAccess } if (tag == nsAccessibilityAtoms::dt || (tag == nsAccessibilityAtoms::li && aFrame->GetType() != nsAccessibilityAtoms::blockFrame)) { // Normally for li, it is created by the list item frame (in nsBlockFrame) // which knows about the bullet frame; however, in this case the list item // must have been styled using display: foo - nsAccessible* accessible = new nsHTMLLIAccessible(aContent, aWeakShell, - EmptyString()); + nsAccessible* accessible = new nsHTMLLIAccessible(aContent, aWeakShell); NS_IF_ADDREF(accessible); return accessible; } if (tag == nsAccessibilityAtoms::abbr || tag == nsAccessibilityAtoms::acronym || tag == nsAccessibilityAtoms::blockquote || tag == nsAccessibilityAtoms::dd || @@ -1711,39 +1776,16 @@ nsAccessibilityService::RemoveNativeRoot nsApplicationAccessible* applicationAcc = nsAccessNode::GetApplicationAccessible(); if (applicationAcc) applicationAcc->RemoveChild(aAccessible); #endif } -// Called from layout when the frame tree owned by a node changes significantly -nsresult -nsAccessibilityService::InvalidateSubtreeFor(nsIPresShell *aShell, - nsIContent *aChangeContent, - PRUint32 aChangeType) -{ - NS_ASSERTION(aChangeType == nsIAccessibilityService::FRAME_SIGNIFICANT_CHANGE || - aChangeType == nsIAccessibilityService::FRAME_SHOW || - aChangeType == nsIAccessibilityService::FRAME_HIDE || - aChangeType == nsIAccessibilityService::NODE_SIGNIFICANT_CHANGE || - aChangeType == nsIAccessibilityService::NODE_APPEND || - aChangeType == nsIAccessibilityService::NODE_REMOVE, - "Incorrect aEvent passed in"); - - NS_ENSURE_ARG_POINTER(aShell); - - nsDocAccessible *docAccessible = GetDocAccessible(aShell->GetDocument()); - if (docAccessible) - docAccessible->InvalidateCacheSubtree(aChangeContent, aChangeType); - - return NS_OK; -} - //////////////////////////////////////////////////////////////////////////////// // NS_GetAccessibilityService //////////////////////////////////////////////////////////////////////////////// /** * Return accessibility service; creating one if necessary. */ nsresult
--- a/accessible/src/base/nsAccessibilityService.h +++ b/accessible/src/base/nsAccessibilityService.h @@ -79,18 +79,17 @@ public: CreateHTMLGroupboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLHRAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLImageAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLLabelAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> - CreateHTMLLIAccessible(nsIContent* aContent, nsIPresShell* aPresShell, - const nsAString& aBulletText); + CreateHTMLLIAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLListboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLMediaAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLObjectFrameAccessible(nsObjectFrame* aFrame, nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> @@ -106,24 +105,31 @@ public: virtual already_AddRefed<nsAccessible> CreateHyperTextAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateOuterDocAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual nsAccessible* AddNativeRootAccessible(void* aAtkAccessible); virtual void RemoveNativeRootAccessible(nsAccessible* aRootAccessible); - virtual nsresult InvalidateSubtreeFor(nsIPresShell *aPresShell, - nsIContent *aContent, - PRUint32 aChangeType); + virtual void ContentRangeInserted(nsIPresShell* aPresShell, + nsIContent* aContainer, + nsIContent* aStartChild, + nsIContent* aEndChild); + + virtual void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aContainer, + nsIContent* aChild); virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget); virtual void PresShellDestroyed(nsIPresShell* aPresShell); + virtual void RecreateAccessible(nsIPresShell* aPresShell, + nsIContent* aContent); + virtual void FireAccessibleEvent(PRUint32 aEvent, nsAccessible* aTarget); // nsAccessibiltiyService /** * Return true if accessibility service has been shutdown. */ static PRBool IsShutdown() { return gIsShutdown; } @@ -175,21 +181,31 @@ public: */ inline nsAccessible* GetContainerAccessible(nsINode* aNode, nsIWeakReference* aWeakShell) { return GetAccessibleByRule(aNode, aWeakShell, eGetAccForContainer); } /** + * Return cached accessible for the given DOM node or cached container + * accessible if there's no cached accessible for the given node. + */ + nsAccessible* GetCachedAccessibleOrContainer(nsINode* aNode); + + /** * Return the first cached accessible parent of a DOM node. * * @param aDOMNode [in] the DOM node to get an accessible for */ - nsAccessible* GetCachedContainerAccessible(nsINode *aNode); + inline nsAccessible* GetCachedContainerAccessible(nsINode *aNode) + { + return aNode ? + GetCachedAccessibleOrContainer(aNode->GetNodeParent()) : nsnull; + } /** * Initialize an accessible and cache it. The method should be called for * every created accessible. * * @param aAccessible [in] accessible to initialize. * @param aRoleMapEntry [in] the role map entry role the ARIA role or nsnull * if none
--- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -94,16 +94,17 @@ #include "nsIURI.h" #include "nsArrayUtils.h" #include "nsIMutableArray.h" #include "nsIObserverService.h" #include "nsIServiceManager.h" #include "nsWhitespaceTokenizer.h" #include "nsAttrName.h" #include "nsNetUtil.h" +#include "nsIEventStateManager.h" #ifdef NS_DEBUG #include "nsIDOMCharacterData.h" #endif #include "mozilla/unused.h" @@ -680,34 +681,33 @@ nsAccessible::GetStateInternal(PRUint32 *aExtraState = nsIAccessibleStates::EXT_STATE_DEFUNCT; return NS_OK_DEFUNCT_OBJECT; } if (aExtraState) *aExtraState = 0; - // Set STATE_UNAVAILABLE state based on disabled attribute - // The disabled attribute is mostly used in XUL elements and HTML forms, but - // if someone sets it on another attribute, - // it seems reasonable to consider it unavailable - PRBool isDisabled; - if (mContent->IsHTML()) { - // In HTML, just the presence of the disabled attribute means it is disabled, - // therefore disabled="false" indicates disabled! - isDisabled = mContent->HasAttr(kNameSpaceID_None, - nsAccessibilityAtoms::disabled); - } - else { - isDisabled = mContent->AttrValueIs(kNameSpaceID_None, - nsAccessibilityAtoms::disabled, - nsAccessibilityAtoms::_true, - eCaseMatters); - } - if (isDisabled) { + nsEventStates intrinsicState = mContent->IntrinsicState(); + + if (intrinsicState.HasState(NS_EVENT_STATE_INVALID)) + *aState |= nsIAccessibleStates::STATE_INVALID; + + if (intrinsicState.HasState(NS_EVENT_STATE_REQUIRED)) + *aState |= nsIAccessibleStates::STATE_REQUIRED; + + PRBool disabled = mContent->IsHTML() ? + (intrinsicState.HasState(NS_EVENT_STATE_DISABLED)) : + (mContent->AttrValueIs(kNameSpaceID_None, + nsAccessibilityAtoms::disabled, + nsAccessibilityAtoms::_true, + eCaseMatters)); + + // Set unavailable state based on disabled state, otherwise set focus states + if (disabled) { *aState |= nsIAccessibleStates::STATE_UNAVAILABLE; } else if (mContent->IsElement()) { nsIFrame *frame = GetFrame(); if (frame && frame->IsFocusable()) { *aState |= nsIAccessibleStates::STATE_FOCUSABLE; } @@ -980,49 +980,59 @@ void nsAccessible::GetBoundsRect(nsRect& iterContent = nsnull; if (depth == 0) iterContent = iterFrame->GetContent(); } } /* void getBounds (out long x, out long y, out long width, out long height); */ -NS_IMETHODIMP nsAccessible::GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height) +NS_IMETHODIMP +nsAccessible::GetBounds(PRInt32* aX, PRInt32* aY, + PRInt32* aWidth, PRInt32* aHeight) { - // This routine will get the entire rectange for all the frames in this node + NS_ENSURE_ARG_POINTER(aX); + *aX = 0; + NS_ENSURE_ARG_POINTER(aY); + *aY = 0; + NS_ENSURE_ARG_POINTER(aWidth); + *aWidth = 0; + NS_ENSURE_ARG_POINTER(aHeight); + *aHeight = 0; + + if (IsDefunct()) + return NS_ERROR_FAILURE; + + // Flush layout so that all the frame construction, reflow, and styles are + // up-to-date since we rely on frames, and styles when calculating state. + // We don't flush the display because we don't care about painting. + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); + presShell->FlushPendingNotifications(Flush_Layout); + + // This routine will get the entire rectangle for all the frames in this node. // ------------------------------------------------------------------------- // Primary Frame for node // Another frame, same node <- Example // Another frame, same node - nsPresContext *presContext = GetPresContext(); - if (!presContext) - { - *x = *y = *width = *height = 0; - return NS_ERROR_FAILURE; - } - nsRect unionRectTwips; - nsIFrame* aBoundingFrame = nsnull; - GetBoundsRect(unionRectTwips, &aBoundingFrame); // Unions up all primary frames for this node and all siblings after it - if (!aBoundingFrame) { - *x = *y = *width = *height = 0; - return NS_ERROR_FAILURE; - } - - *x = presContext->AppUnitsToDevPixels(unionRectTwips.x); - *y = presContext->AppUnitsToDevPixels(unionRectTwips.y); - *width = presContext->AppUnitsToDevPixels(unionRectTwips.width); - *height = presContext->AppUnitsToDevPixels(unionRectTwips.height); + nsIFrame* boundingFrame = nsnull; + GetBoundsRect(unionRectTwips, &boundingFrame); // Unions up all primary frames for this node and all siblings after it + NS_ENSURE_STATE(boundingFrame); + + nsPresContext* presContext = presShell->GetPresContext(); + *aX = presContext->AppUnitsToDevPixels(unionRectTwips.x); + *aY = presContext->AppUnitsToDevPixels(unionRectTwips.y); + *aWidth = presContext->AppUnitsToDevPixels(unionRectTwips.width); + *aHeight = presContext->AppUnitsToDevPixels(unionRectTwips.height); // We have the union of the rectangle, now we need to put it in absolute screen coords - - nsIntRect orgRectPixels = aBoundingFrame->GetScreenRectExternal(); - *x += orgRectPixels.x; - *y += orgRectPixels.y; + nsIntRect orgRectPixels = boundingFrame->GetScreenRectExternal(); + *aX += orgRectPixels.x; + *aY += orgRectPixels.y; return NS_OK; } // helpers nsIFrame* nsAccessible::GetBoundsFrame() { @@ -2614,38 +2624,31 @@ nsAccessible::AppendTextTo(nsAString& aT // nsAccessNode public methods PRBool nsAccessible::Init() { if (!nsAccessNodeWrap::Init()) return PR_FALSE; - nsDocAccessible *docAcc = + nsDocAccessible* document = GetAccService()->GetDocAccessible(mContent->GetOwnerDoc()); - NS_ASSERTION(docAcc, "Cannot cache new nsAccessible!"); - if (!docAcc) - return PR_FALSE; - - void *uniqueID = nsnull; - GetUniqueID(&uniqueID); - - return docAcc->CacheAccessible(uniqueID, this); + NS_ASSERTION(document, "Cannot cache new nsAccessible!"); + + return document ? document->CacheAccessible(this) : PR_FALSE; } void nsAccessible::Shutdown() { // Invalidate the child count and pointers to other accessibles, also make // sure none of its children point to this parent InvalidateChildren(); - if (mParent) { - mParent->InvalidateChildren(); - UnbindFromParent(); - } + if (mParent) + mParent->RemoveChild(this); nsAccessNodeWrap::Shutdown(); } //////////////////////////////////////////////////////////////////////////////// // nsAccessible public methods nsresult @@ -2684,24 +2687,24 @@ nsAccessible::GetNameInternal(nsAString& } // nsAccessible protected void nsAccessible::BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent) { NS_PRECONDITION(aParent, "This method isn't used to set null parent!"); - if (mParent && mParent != aParent) { - // Adopt a child -- we allow this now. the new parent - // may be a dom node which wasn't previously accessible but now is. - // The old parent's children now need to be invalidated, since - // it no longer owns the child, the new parent does - NS_ASSERTION(PR_FALSE, "Adopting child!"); - if (mParent) + if (mParent) { + if (mParent != aParent) { + NS_ERROR("Adopting child!"); mParent->InvalidateChildren(); + } else { + NS_ERROR("Binding to the same parent!"); + return; + } } mParent = aParent; mIndexInParent = aIndexInParent; } void nsAccessible::UnbindFromParent() @@ -2758,16 +2761,23 @@ nsAccessible::InsertChildAt(PRUint32 aIn } PRBool nsAccessible::RemoveChild(nsAccessible* aChild) { if (aChild->mParent != this || aChild->mIndexInParent == -1) return PR_FALSE; + if (aChild->mIndexInParent >= mChildren.Length() || + mChildren[aChild->mIndexInParent] != aChild) { + NS_ERROR("Child is bound to parent but parent hasn't this child at its index!"); + aChild->UnbindFromParent(); + return PR_FALSE; + } + for (PRUint32 idx = aChild->mIndexInParent + 1; idx < mChildren.Length(); idx++) mChildren[idx]->mIndexInParent--; mChildren.RemoveElementAt(aChild->mIndexInParent); mEmbeddedObjCollector = nsnull; aChild->UnbindFromParent(); return PR_TRUE; @@ -2892,23 +2902,20 @@ nsAccessible::GetIndexOfEmbeddedChild(ns } #ifdef DEBUG PRBool nsAccessible::IsInCache() { nsDocAccessible *docAccessible = GetAccService()->GetDocAccessible(mContent->GetOwnerDoc()); - if (!docAccessible) - return nsnull; - - void *uniqueID = nsnull; - GetUniqueID(&uniqueID); - - return docAccessible->GetCachedAccessible(uniqueID) ? PR_TRUE : PR_FALSE; + if (docAccessible) + return docAccessible->GetCachedAccessibleByUniqueID(UniqueID()) ? PR_TRUE : PR_FALSE; + + return PR_FALSE; } #endif //////////////////////////////////////////////////////////////////////////////// // HyperLinkAccessible methods bool
--- a/accessible/src/base/nsAccessible.h +++ b/accessible/src/base/nsAccessible.h @@ -46,31 +46,34 @@ #include "nsIAccessibleSelectable.h" #include "nsIAccessibleValue.h" #include "nsIAccessibleRole.h" #include "nsIAccessibleStates.h" #include "nsStringGlue.h" #include "nsTArray.h" #include "nsRefPtrHashtable.h" +#include "nsDataHashtable.h" class AccGroupInfo; class EmbeddedObjCollector; class nsAccessible; class AccEvent; struct nsRoleMapEntry; struct nsRect; class nsIContent; class nsIFrame; class nsIAtom; class nsIView; typedef nsRefPtrHashtable<nsVoidPtrHashKey, nsAccessible> nsAccessibleHashtable; +typedef nsDataHashtable<nsPtrHashKey<const nsINode>, nsAccessible*> + NodeToAccessibleMap; // see nsAccessible::GetAttrValue #define NS_OK_NO_ARIA_VALUE \ NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_GENERAL, 0x21) // see nsAccessible::GetNameInternal #define NS_OK_EMPTY_NAME \ NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_GENERAL, 0x23) @@ -201,16 +204,17 @@ public: /** * Set the ARIA role map entry for a new accessible. * For a newly created accessible, specify which role map entry should be used. * * @param aRoleMapEntry The ARIA nsRoleMapEntry* for the accessible, or * nsnull if none. */ virtual void SetRoleMapEntry(nsRoleMapEntry *aRoleMapEntry); + const nsRoleMapEntry* GetRoleMapEntry() const { return mRoleMapEntry; } /** * Cache children if necessary. Return true if the accessible is defunct. */ PRBool EnsureChildren(); /** * Set the child count to -1 (unknown) and null out cached child pointers. @@ -285,17 +289,19 @@ public: mParent->mChildren.SafeElementAt(mIndexInParent + 1, nsnull).get() : nsnull; } nsAccessible* GetCachedPrevSibling() const { return mParent ? mParent->mChildren.SafeElementAt(mIndexInParent - 1, nsnull).get() : nsnull; } PRUint32 GetCachedChildCount() const { return mChildren.Length(); } + nsAccessible* GetCachedChildAt(PRUint32 aIndex) const { return mChildren.ElementAt(aIndex); } PRBool AreChildrenCached() const { return mChildrenFlags != eChildrenUninitialized; } + bool IsBoundToParent() const { return mParent; } #ifdef DEBUG /** * Return true if the access node is cached. */ PRBool IsInCache(); #endif
--- a/accessible/src/base/nsApplicationAccessible.cpp +++ b/accessible/src/base/nsApplicationAccessible.cpp @@ -358,16 +358,22 @@ nsApplicationAccessible::Init() } void nsApplicationAccessible::Shutdown() { mAppInfo = nsnull; } +bool +nsApplicationAccessible::IsPrimaryForNode() const +{ + return false; +} + //////////////////////////////////////////////////////////////////////////////// // nsAccessible public methods nsresult nsApplicationAccessible::GetARIAState(PRUint32 *aState, PRUint32 *aExtraState) { return NS_OK; } @@ -465,17 +471,17 @@ nsApplicationAccessible::GetSiblingAtOff if (aError) *aError = NS_OK; // fail peacefully return nsnull; } //////////////////////////////////////////////////////////////////////////////// -// nsIAccessNode +// nsIAccessNode and nsAccessNode NS_IMETHODIMP nsApplicationAccessible::GetDOMNode(nsIDOMNode **aDOMNode) { NS_ENSURE_ARG_POINTER(aDOMNode); *aDOMNode = nsnull; return NS_OK; } @@ -520,24 +526,16 @@ NS_IMETHODIMP nsApplicationAccessible::GetOwnerWindow(void **aOwnerWindow) { NS_ENSURE_ARG_POINTER(aOwnerWindow); *aOwnerWindow = nsnull; return NS_OK; } NS_IMETHODIMP -nsApplicationAccessible::GetUniqueID(void **aUniqueID) -{ - NS_ENSURE_ARG_POINTER(aUniqueID); - *aUniqueID = static_cast<void *>(this); - return NS_OK; -} - -NS_IMETHODIMP nsApplicationAccessible::GetComputedStyleValue(const nsAString &aPseudoElt, const nsAString &aPropertyName, nsAString &aValue) { return NS_OK; } NS_IMETHODIMP @@ -551,8 +549,9 @@ nsApplicationAccessible::GetComputedStyl } NS_IMETHODIMP nsApplicationAccessible::GetLanguage(nsAString &aLanguage) { aLanguage.Truncate(); return NS_OK; } +
--- a/accessible/src/base/nsApplicationAccessible.h +++ b/accessible/src/base/nsApplicationAccessible.h @@ -66,17 +66,30 @@ public: using nsAccessible::GetChildAtPoint; nsApplicationAccessible(); // nsISupports NS_DECL_ISUPPORTS_INHERITED // nsIAccessNode - NS_DECL_NSIACCESSNODE + NS_SCRIPTABLE NS_IMETHOD GetDOMNode(nsIDOMNode** aDOMNode); + NS_SCRIPTABLE NS_IMETHOD GetDocument(nsIAccessibleDocument** aDocument); + NS_SCRIPTABLE NS_IMETHOD GetRootDocument(nsIAccessibleDocument** aRootDocument); + NS_SCRIPTABLE NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML); + NS_SCRIPTABLE NS_IMETHOD ScrollTo(PRUint32 aScrollType); + NS_SCRIPTABLE NS_IMETHOD ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY); + NS_IMETHOD GetOwnerWindow(void **aOwnerWindow); + NS_SCRIPTABLE NS_IMETHOD GetComputedStyleValue(const nsAString& aPseudoElt, + const nsAString& aPropertyName, + nsAString& aValue NS_OUTPARAM); + NS_SCRIPTABLE NS_IMETHOD GetComputedStyleCSSValue(const nsAString& aPseudoElt, + const nsAString& aPropertyName, + nsIDOMCSSPrimitiveValue** aValue NS_OUTPARAM); + NS_SCRIPTABLE NS_IMETHOD GetLanguage(nsAString& aLanguage); // nsIAccessible NS_IMETHOD GetParent(nsIAccessible **aParent); NS_IMETHOD GetNextSibling(nsIAccessible **aNextSibling); NS_IMETHOD GetPreviousSibling(nsIAccessible **aPreviousSibling); NS_IMETHOD GetName(nsAString &aName); NS_IMETHOD GetValue(nsAString &aValue); NS_IMETHOD GetDescription(nsAString &aDescription); @@ -104,16 +117,17 @@ public: // nsIAccessibleApplication NS_DECL_NSIACCESSIBLEAPPLICATION // nsAccessNode virtual PRBool IsDefunct(); virtual PRBool Init(); virtual void Shutdown(); + virtual bool IsPrimaryForNode() const; // nsAccessible virtual nsresult GetARIAState(PRUint32 *aState, PRUint32 *aExtraState); virtual PRUint32 Role(); virtual PRUint32 NativeRole(); virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState); virtual void InvalidateChildren();
--- a/accessible/src/base/nsCaretAccessible.cpp +++ b/accessible/src/base/nsCaretAccessible.cpp @@ -280,18 +280,17 @@ nsCaretAccessible::SpellcheckSelectionCh // misspelled word). If spellchecking is disabled (for example, // @spellcheck="false" on html:body) then we won't fire any event. nsRefPtr<nsHyperTextAccessible> textAcc = nsAccUtils::GetTextAccessibleFromSelection(aSel); NS_ENSURE_STATE(textAcc); nsRefPtr<AccEvent> event = - new AccEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED, - textAcc, nsnull); + new AccEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED, textAcc); nsEventShell::FireEvent(event); return NS_OK; } nsIntRect nsCaretAccessible::GetCaretRect(nsIWidget **aOutWidget) {
--- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -79,30 +79,30 @@ #endif namespace dom = mozilla::dom; //////////////////////////////////////////////////////////////////////////////// // Static member initialization PRUint32 nsDocAccessible::gLastFocusedAccessiblesState = 0; -nsIAtom *nsDocAccessible::gLastFocusedFrameType = nsnull; //////////////////////////////////////////////////////////////////////////////// // Constructor/desctructor nsDocAccessible:: nsDocAccessible(nsIDocument *aDocument, nsIContent *aRootContent, nsIWeakReference *aShell) : nsHyperTextAccessibleWrap(aRootContent, aShell), mDocument(aDocument), mScrollPositionChangedTicks(0), mIsLoaded(PR_FALSE) { // XXX aaronl should we use an algorithm for the initial cache size? mAccessibleCache.Init(kDefaultCacheSize); + mNodeToAccessibleMap.Init(kDefaultCacheSize); // For GTK+ native window, we do nothing here. if (!mDocument) return; // nsAccDocManager creates document accessible when scrollable frame is // available already, it should be safe time to add scroll listener. AddScrollListener(); @@ -129,16 +129,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_ } CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventQueue) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments) + tmp->mNodeToAccessibleMap.Clear(); ClearCache(tmp->mAccessibleCache); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible) NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsDocAccessible) NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument) NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver) NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) @@ -563,26 +564,24 @@ NS_IMETHODIMP nsDocAccessible::GetAssoci if (isEditable) { NS_ADDREF(*aEditor = editor); } return NS_OK; } // nsDocAccessible public method nsAccessible * -nsDocAccessible::GetCachedAccessible(void *aUniqueID) +nsDocAccessible::GetCachedAccessible(nsINode *aNode) { - nsAccessible* accessible = mAccessibleCache.GetWeak(aUniqueID); + nsAccessible* accessible = mNodeToAccessibleMap.Get(aNode); // No accessible in the cache, check if the given ID is unique ID of this // document accessible. if (!accessible) { - void* thisUniqueID = nsnull; - GetUniqueID(&thisUniqueID); - if (thisUniqueID != aUniqueID) + if (GetNode() != aNode) return nsnull; accessible = this; } #ifdef DEBUG // All cached accessible nodes should be in the parent // It will assert if not all the children were created @@ -593,39 +592,36 @@ nsDocAccessible::GetCachedAccessible(voi parent->TestChildCache(accessible); #endif return accessible; } // nsDocAccessible public method PRBool -nsDocAccessible::CacheAccessible(void *aUniqueID, nsAccessible *aAccessible) +nsDocAccessible::CacheAccessible(nsAccessible* aAccessible) { - // If there is already an accessible with the given unique ID, shut it down - // because the DOM node has changed. - nsAccessible *accessible = mAccessibleCache.GetWeak(aUniqueID); - NS_ASSERTION(!accessible, - "Caching new accessible for the DOM node while the old one is alive"); + if (aAccessible->IsPrimaryForNode() && + !mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible)) + return PR_FALSE; - if (accessible) - accessible->Shutdown(); - - return mAccessibleCache.Put(aUniqueID, aAccessible); + return mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible); } // nsDocAccessible public method void -nsDocAccessible::RemoveAccessNodeFromCache(nsAccessible *aAccessible) +nsDocAccessible::ShutdownAccessible(nsAccessible *aAccessible) { - if (!aAccessible) - return; + // Remove an accessible from node to accessible map if it is presented there. + if (aAccessible->IsPrimaryForNode() && + mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible) + mNodeToAccessibleMap.Remove(aAccessible->GetNode()); - void *uniqueID = nsnull; - aAccessible->GetUniqueID(&uniqueID); + void* uniqueID = aAccessible->UniqueID(); + aAccessible->Shutdown(); mAccessibleCache.Remove(uniqueID); } //////////////////////////////////////////////////////////////////////////////// // nsAccessNode PRBool nsDocAccessible::Init() @@ -641,22 +637,24 @@ nsDocAccessible::Init() nsDocAccessible* parentDocument = mParent->GetDocAccessible(); if (parentDocument) parentDocument->AppendChildDocument(this); // Fire reorder event to notify new accessible document has been created and // attached to the tree. nsRefPtr<AccEvent> reorderEvent = - new AccReorderEvent(mParent, PR_FALSE, PR_TRUE, mDocument); - if (!reorderEvent) - return PR_FALSE; + new AccEvent(nsIAccessibleEvent::EVENT_REORDER, mParent, eAutoDetect, + AccEvent::eCoalesceFromSameSubtree); + if (reorderEvent) { + FireDelayedAccessibleEvent(reorderEvent); + return PR_TRUE; + } - FireDelayedAccessibleEvent(reorderEvent); - return PR_TRUE; + return PR_FALSE; } void nsDocAccessible::Shutdown() { if (!mWeakShell) // already shutdown return; @@ -672,26 +670,33 @@ nsDocAccessible::Shutdown() if (mParent) { nsDocAccessible* parentDocument = mParent->GetDocAccessible(); if (parentDocument) parentDocument->RemoveChildDocument(this); mParent->RemoveChild(this); } + PRUint32 childDocCount = mChildDocuments.Length(); + for (PRUint32 idx = 0; idx < childDocCount; idx++) + mChildDocuments[idx]->Shutdown(); + mChildDocuments.Clear(); mWeakShell = nsnull; // Avoid reentrancy + mNodeToAccessibleMap.Clear(); ClearCache(mAccessibleCache); nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument; mDocument = nsnull; nsHyperTextAccessibleWrap::Shutdown(); + + GetAccService()->NotifyOfDocumentShutdown(kungFuDeathGripDoc); } nsIFrame* nsDocAccessible::GetFrame() { nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell)); nsIFrame* root = nsnull; @@ -1039,18 +1044,17 @@ nsDocAccessible::AttributeChangedImpl(ns } if (aAttribute == nsAccessibilityAtoms::role || aAttribute == nsAccessibilityAtoms::href || aAttribute == nsAccessibilityAtoms::onclick) { // Not worth the expense to ensure which namespace these are in // It doesn't kill use to recreate the accessible even if the attribute was used // in the wrong namespace or an element that doesn't support it - InvalidateCacheSubtree(aContent, - nsIAccessibilityService::NODE_SIGNIFICANT_CHANGE); + RecreateAccessible(aContent); return; } if (aAttribute == nsAccessibilityAtoms::alt || aAttribute == nsAccessibilityAtoms::title || aAttribute == nsAccessibilityAtoms::aria_label || aAttribute == nsAccessibilityAtoms::aria_labelledby) { FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, @@ -1213,59 +1217,43 @@ nsDocAccessible::ARIAAttributeChanged(ns return; } if (aAttribute == nsAccessibilityAtoms::aria_multiselectable && aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::role)) { // This affects whether the accessible supports SelectAccessible. // COM says we cannot change what interfaces are supported on-the-fly, // so invalidate this object. A new one will be created on demand. - InvalidateCacheSubtree(aContent, - nsIAccessibilityService::NODE_SIGNIFICANT_CHANGE); + RecreateAccessible(aContent); return; } } void nsDocAccessible::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aFirstNewContent, PRInt32 /* unused */) { - if (!IsContentLoaded() && mAccessibleCache.Count() <= 1) { - // See comments in nsDocAccessible::InvalidateCacheSubtree - InvalidateChildren(); - return; - } - - // Does this need to be a strong ref? If so, why? - for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) { - // InvalidateCacheSubtree will not fire the EVENT_SHOW for the new node - // unless an accessible can be created for the passed in node, which it - // can't do unless the node is visible. The right thing happens there so - // no need for an extra visibility check here. - InvalidateCacheSubtree(cur, nsIAccessibilityService::NODE_APPEND); - } } - void nsDocAccessible::ContentStatesChanged(nsIDocument* aDocument, nsIContent* aContent1, nsIContent* aContent2, - PRInt32 aStateMask) + nsEventStates aStateMask) { - if (0 == (aStateMask & NS_EVENT_STATE_CHECKED)) { + if (!aStateMask.HasState(NS_EVENT_STATE_CHECKED)) { return; } nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent1); nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent2); } void nsDocAccessible::DocumentStatesChanged(nsIDocument* aDocument, - PRInt32 aStateMask) + nsEventStates aStateMask) { } void nsDocAccessible::CharacterDataWillChange(nsIDocument *aDocument, nsIContent* aContent, CharacterDataChangeInfo* aInfo) { FireTextChangeEventForText(aContent, aInfo, PR_FALSE); @@ -1277,34 +1265,23 @@ void nsDocAccessible::CharacterDataChang { FireTextChangeEventForText(aContent, aInfo, PR_TRUE); } void nsDocAccessible::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aChild, PRInt32 /* unused */) { - // InvalidateCacheSubtree will not fire the EVENT_SHOW for the new node - // unless an accessible can be created for the passed in node, which it - // can't do unless the node is visible. The right thing happens there so - // no need for an extra visibility check here. - InvalidateCacheSubtree(aChild, nsIAccessibilityService::NODE_APPEND); } void nsDocAccessible::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aChild, PRInt32 /* unused */, nsIContent* aPreviousSibling) { - // It's no needed to invalidate the subtree of the removed element, - // because we get notifications directly from content (see - // nsGenericElement::doRemoveChildAt) *before* the frame for the content is - // destroyed, or any other side effects occur . That allows us to correctly - // calculate the TEXT_REMOVED event if there is one and coalesce events from - // the same subtree. } void nsDocAccessible::ParentChainChanged(nsIContent *aContent) { } @@ -1335,46 +1312,190 @@ nsDocAccessible::GetNativeWindow() const vm->GetRootWidget(getter_AddRefs(widget)); if (widget) return widget->GetNativeData(NS_NATIVE_WINDOW); } return nsnull; } nsAccessible* -nsDocAccessible::GetCachedAccessibleInSubtree(void* aUniqueID) +nsDocAccessible::GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID) { - nsAccessible* child = GetCachedAccessible(aUniqueID); + nsAccessible* child = GetCachedAccessibleByUniqueID(aUniqueID); if (child) return child; PRUint32 childDocCount = mChildDocuments.Length(); for (PRUint32 childDocIdx= 0; childDocIdx < childDocCount; childDocIdx++) { nsDocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx); - child = childDocument->GetCachedAccessibleInSubtree(aUniqueID); + child = childDocument->GetCachedAccessibleByUniqueIDInSubtree(aUniqueID); if (child) return child; } return nsnull; } +void +nsDocAccessible::UpdateTree(nsIContent* aContainerNode, + nsIContent* aStartNode, + nsIContent* aEndNode, + PRBool aIsInsert) +{ + // Content change notification mostly are async, thus we can't detect whether + // these actions are from user. This information is used to fire or do not + // fire events to avoid events that are generated because of document loading. + // Since this information may be not correct then we need to fire some events + // regardless the document loading state. + + nsCOMPtr<nsIPresShell> presShell = GetPresShell(); + nsIEventStateManager* esm = presShell->GetPresContext()->EventStateManager(); + PRBool fireAllEvents = PR_TRUE;//IsContentLoaded() || esm->IsHandlingUserInputExternal(); + + // We don't create new accessibles on content removal. + nsAccessible* container = aIsInsert ? + GetAccService()->GetAccessibleOrContainer(aContainerNode, mWeakShell) : + GetAccService()->GetCachedAccessibleOrContainer(aContainerNode); + + if (aIsInsert) { + // XXX: Invalidate parent-child relations for container accessible and its + // children because there's no good way to find insertion point of new child + // accessibles into accessible tree. We need to invalidate children even + // there's no inserted accessibles in the end because accessible children + // are created while parent recaches child accessibles. + container->InvalidateChildren(); + } + + EIsFromUserInput fromUserInput = esm->IsHandlingUserInputExternal() ? + eFromUserInput : eNoUserInput; + + // Update the accessible tree in the case of content removal and fire events + // if allowed. + PRUint32 updateFlags = + UpdateTreeInternal(container, aStartNode, aEndNode, + aIsInsert, fireAllEvents, fromUserInput); + + // Content insertion/removal is not cause of accessible tree change. + if (updateFlags == eNoAccessible) + return; + + // Check to see if change occurred inside an alert, and fire an EVENT_ALERT + // if it did. + if (aIsInsert && !(updateFlags & eAlertAccessible)) { + // XXX: tree traversal is perf issue, accessible should know if they are + // children of alert accessible to avoid this. + nsAccessible* ancestor = container; + while (ancestor) { + const nsRoleMapEntry* roleMapEntry = ancestor->GetRoleMapEntry(); + if (roleMapEntry && roleMapEntry->role == nsIAccessibleRole::ROLE_ALERT) { + FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT, + ancestor->GetNode(), AccEvent::eRemoveDupes, + fromUserInput); + break; + } + + // Don't climb above this document. + if (ancestor == this) + break; + + ancestor = ancestor->GetParent(); + } + } + + // Fire nether value change nor reorder events if action is not from user + // input and document is loading. We are notified about changes in editor + // synchronously, so from user input flag is correct for value change events. + if (!fireAllEvents) + return; + + // Fire value change event. + if (container->Role() == nsIAccessibleRole::ROLE_ENTRY) { + nsRefPtr<AccEvent> valueChangeEvent = + new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, container, + fromUserInput, AccEvent::eRemoveDupes); + FireDelayedAccessibleEvent(valueChangeEvent); + } + + // Fire reorder event so the MSAA clients know the children have changed. Also + // the event is used internally by MSAA part. + nsRefPtr<AccEvent> reorderEvent = + new AccEvent(nsIAccessibleEvent::EVENT_REORDER, container->GetNode(), + fromUserInput, AccEvent::eCoalesceFromSameSubtree); + if (reorderEvent) + FireDelayedAccessibleEvent(reorderEvent); +} + +void +nsDocAccessible::RecreateAccessible(nsINode* aNode) +{ + // XXX: we shouldn't recreate whole accessible subtree that happens when + // hide event is handled, instead we should subclass hide and show events + // to handle them separately and implement their coalescence with normal hide + // and show events. + + nsAccessible* parent = nsnull; + + // Fire hide event for old accessible. + nsAccessible* oldAccessible = + GetAccService()->GetAccessibleInWeakShell(aNode, mWeakShell); + if (oldAccessible) { + parent = oldAccessible->GetParent(); + + nsRefPtr<AccEvent> hideEvent = new AccHideEvent(oldAccessible, aNode, + eAutoDetect); + if (hideEvent) + FireDelayedAccessibleEvent(hideEvent); + + // Unbind old accessible from tree. + parent->RemoveChild(oldAccessible); + + if (oldAccessible->IsPrimaryForNode() && + mNodeToAccessibleMap.Get(oldAccessible->GetNode()) == oldAccessible) + mNodeToAccessibleMap.Remove(oldAccessible->GetNode()); + + } else { + parent = GetAccService()->GetContainerAccessible(aNode, mWeakShell); + } + + // Get new accessible and fire show event. + parent->InvalidateChildren(); + + nsAccessible* newAccessible = + GetAccService()->GetAccessibleInWeakShell(aNode, mWeakShell); + if (newAccessible) { + nsRefPtr<AccEvent> showEvent = new AccShowEvent(newAccessible, aNode, + eAutoDetect); + if (showEvent) + FireDelayedAccessibleEvent(showEvent); + } + + // Fire reorder event. + if (oldAccessible || newAccessible) { + nsRefPtr<AccEvent> reorderEvent = + new AccEvent(nsIAccessibleEvent::EVENT_REORDER, parent->GetNode(), + eAutoDetect, AccEvent::eCoalesceFromSameSubtree); + + if (reorderEvent) + FireDelayedAccessibleEvent(reorderEvent); + } +} + //////////////////////////////////////////////////////////////////////////////// // Protected members void nsDocAccessible::FireValueChangeForTextFields(nsAccessible *aAccessible) { if (aAccessible->Role() != nsIAccessibleRole::ROLE_ENTRY) return; // Dependent value change event for text changes in textfields nsRefPtr<AccEvent> valueChangeEvent = new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible, - PR_FALSE, eAutoDetect, AccEvent::eRemoveDupes); + eAutoDetect, AccEvent::eRemoveDupes); FireDelayedAccessibleEvent(valueChangeEvent); } void nsDocAccessible::FireTextChangeEventForText(nsIContent *aContent, CharacterDataChangeInfo* aInfo, PRBool aIsInserted) { @@ -1421,106 +1542,30 @@ nsDocAccessible::FireTextChangeEventForT if (text.IsEmpty()) return; // Normally we only fire delayed events created from the node, not an // accessible object. See the AccTextChangeEvent constructor for details // about this exceptional case. nsRefPtr<AccEvent> event = new AccTextChangeEvent(textAccessible, offset + textOffset, text, - aIsInserted, PR_FALSE); + aIsInserted); FireDelayedAccessibleEvent(event); FireValueChangeForTextFields(textAccessible); } -already_AddRefed<AccEvent> -nsDocAccessible::CreateTextChangeEventForNode(nsAccessible *aContainerAccessible, - nsIContent *aChangeNode, - nsAccessible *aChangeChild, - PRBool aIsInserting, - PRBool aIsAsynch, - EIsFromUserInput aIsFromUserInput) -{ - nsRefPtr<nsHyperTextAccessible> textAccessible = - do_QueryObject(aContainerAccessible); - if (!textAccessible) { - return nsnull; - } - - nsAutoString text; - PRInt32 offset = 0; - if (aChangeChild) { - // Don't fire event for the first html:br in an editor. - if (aChangeChild->Role() == nsIAccessibleRole::ROLE_WHITESPACE) { - nsCOMPtr<nsIEditor> editor; - textAccessible->GetAssociatedEditor(getter_AddRefs(editor)); - if (editor) { - PRBool isEmpty = PR_FALSE; - editor->GetDocumentIsEmpty(&isEmpty); - if (isEmpty) { - return nsnull; - } - } - } - - offset = textAccessible->GetChildOffset(aChangeChild); - aChangeChild->AppendTextTo(text, 0, PR_UINT32_MAX); - - } else { - // A span-level object or something else without an accessible is being - // added, where it has no accessible but it has descendant content which is - // aggregated as text into the parent hypertext. In this case, changed text - // is compounded from all accessible contained in changed node. - nsAccTreeWalker walker(mWeakShell, aChangeNode, - GetAllowsAnonChildAccessibles()); - nsRefPtr<nsAccessible> child = walker.GetNextChild(); - - // No descendant content that represents any text in the hypertext parent. - if (!child) - return nsnull; - - offset = textAccessible->GetChildOffset(child); - child->AppendTextTo(text, 0, PR_UINT32_MAX); - - nsINode* containerNode = textAccessible->GetNode(); - PRInt32 childCount = textAccessible->GetChildCount(); - PRInt32 childIdx = child->GetIndexInParent(); - - for (PRInt32 idx = childIdx + 1; idx < childCount; idx++) { - nsAccessible* nextChild = textAccessible->GetChildAt(idx); - // We only want accessibles with DOM nodes as children of this node. - if (!nsCoreUtils::IsAncestorOf(aChangeNode, nextChild->GetNode(), - containerNode)) - break; - - nextChild->AppendTextTo(text, 0, PR_UINT32_MAX); - } - } - - if (text.IsEmpty()) - return nsnull; - - AccEvent* event = new AccTextChangeEvent(aContainerAccessible, offset, text, - aIsInserting, aIsAsynch, - aIsFromUserInput); - NS_IF_ADDREF(event); - - return event; -} - // nsDocAccessible public member nsresult nsDocAccessible::FireDelayedAccessibleEvent(PRUint32 aEventType, nsINode *aNode, AccEvent::EEventRule aAllowDupes, - PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput) { nsRefPtr<AccEvent> event = - new AccEvent(aEventType, aNode, aIsAsynch, aIsFromUserInput, aAllowDupes); + new AccEvent(aEventType, aNode, aIsFromUserInput, aAllowDupes); NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY); return FireDelayedAccessibleEvent(event); } // nsDocAccessible public member nsresult nsDocAccessible::FireDelayedAccessibleEvent(AccEvent* aEvent) @@ -1529,509 +1574,174 @@ nsDocAccessible::FireDelayedAccessibleEv NS_LOG_ACCDOCLOAD_FIREEVENT(aEvent) if (mEventQueue) mEventQueue->Push(aEvent); return NS_OK; } +// nsDocAccessible public member void nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent) -{ - nsAccessible *accessible = aEvent->GetAccessible(); - nsINode *node = aEvent->GetNode(); - - PRUint32 eventType = aEvent->GetEventType(); - EIsFromUserInput isFromUserInput = - aEvent->IsFromUserInput() ? eFromUserInput : eNoUserInput; - - PRBool isAsync = aEvent->IsAsync(); - - if (node == gLastFocusedNode && isAsync && - (eventType == nsIAccessibleEvent::EVENT_SHOW || - eventType == nsIAccessibleEvent::EVENT_HIDE)) { - // If frame type didn't change for this event, then we don't actually need to invalidate - // However, we only keep track of the old frame type for the focus, where it's very - // important not to destroy and recreate the accessible for minor style changes, - // such as a:focus { overflow: scroll; } - nsCOMPtr<nsIContent> focusContent(do_QueryInterface(node)); - if (focusContent) { - nsIFrame *focusFrame = focusContent->GetPrimaryFrame(); - nsIAtom *newFrameType = - (focusFrame && focusFrame->GetStyleVisibility()->IsVisible()) ? - focusFrame->GetType() : nsnull; - - if (newFrameType == gLastFocusedFrameType) { - // Don't need to invalidate this current accessible, but can - // just invalidate the children instead - FireShowHideEvents(node, PR_TRUE, eventType, eNormalEvent, - isAsync, isFromUserInput); - return; - } - gLastFocusedFrameType = newFrameType; - } - } - - if (eventType == nsIAccessibleEvent::EVENT_SHOW) { - - nsAccessible* containerAccessible = nsnull; - if (accessible) { - containerAccessible = accessible->GetParent(); - } else { - nsCOMPtr<nsIWeakReference> weakShell(nsCoreUtils::GetWeakShellFor(node)); - containerAccessible = GetAccService()->GetContainerAccessible(node, - weakShell); - } - - if (!containerAccessible) - containerAccessible = this; - - if (isAsync) { - // For asynch show, delayed invalidatation of parent's children - containerAccessible->InvalidateChildren(); - - // Some show events in the subtree may have been removed to - // avoid firing redundant events. But, we still need to make sure any - // accessibles parenting those shown nodes lose their child references. - InvalidateChildrenInSubtree(node); - } - - // Also fire text changes if the node being created could affect the text in an nsIAccessibleText parent. - // When a node is being made visible or is inserted, the text in an ancestor hyper text will gain characters - // At this point we now have the frame and accessible for this node if there is one. That is why we - // wait to fire this here, instead of in InvalidateCacheSubtree(), where we wouldn't be able to calculate - // the offset, length and text for the text change. - if (node && node != mDocument) { - nsCOMPtr<nsIContent> content(do_QueryInterface(node)); - nsRefPtr<AccEvent> textChangeEvent = - CreateTextChangeEventForNode(containerAccessible, content, accessible, - PR_TRUE, PR_TRUE, isFromUserInput); - if (textChangeEvent) { - // XXX Queue them up and merge the text change events - // XXX We need a way to ignore SplitNode and JoinNode() when they - // do