From a549264d0c29f42707ca5cb0e1204a7de2c977c0 Mon Sep 17 00:00:00 2001 From: "aaronleventhal@moonset.net" Date: Fri, 10 Aug 2007 18:44:44 -0700 Subject: [PATCH] Bug 390692. Fix live region support to provide useful information on the node that's being shown or hidden, or has a text change event. r=ginn.chen, sr=roc, a=roc --- accessible/public/nsIAccessibleEvent.idl | 31 ++-- accessible/src/atk/nsAccessibleWrap.cpp | 21 ++- .../src/base/nsAccessibilityService.cpp | 6 +- accessible/src/base/nsAccessibilityUtils.cpp | 5 +- accessible/src/base/nsAccessibilityUtils.h | 3 +- accessible/src/base/nsAccessible.cpp | 17 +- accessible/src/base/nsAccessibleEventData.cpp | 169 +++++++++++++++++- accessible/src/base/nsAccessibleEventData.h | 31 +++- accessible/src/base/nsDocAccessible.cpp | 143 +++++++++------ accessible/src/base/nsDocAccessible.h | 24 ++- accessible/src/base/nsRootAccessible.cpp | 26 +-- accessible/src/base/nsRootAccessible.h | 3 +- accessible/src/html/nsHyperTextAccessible.cpp | 11 +- accessible/src/msaa/nsAccessibleWrap.cpp | 3 +- accessible/src/msaa/nsEventMap.h | 12 +- .../src/msaa/nsHyperTextAccessibleWrap.cpp | 3 +- content/events/public/nsIEventStateManager.h | 17 +- content/events/src/nsEventStateManager.h | 2 + layout/base/nsCSSFrameConstructor.cpp | 5 +- layout/base/nsFrameManager.cpp | 4 +- layout/base/nsPresShell.cpp | 2 +- 21 files changed, 404 insertions(+), 134 deletions(-) diff --git a/accessible/public/nsIAccessibleEvent.idl b/accessible/public/nsIAccessibleEvent.idl index 14568ef731b..e45f5e5ac32 100755 --- a/accessible/public/nsIAccessibleEvent.idl +++ b/accessible/public/nsIAccessibleEvent.idl @@ -61,38 +61,41 @@ interface nsIDOMNode; * * @status UNDER_REVIEW */ -[scriptable, uuid(18612bcb-79bd-45c1-92e9-07aded5fd0f5)] +[scriptable, uuid(98f9e2d4-ec22-4601-b927-b9faf7a63248)] interface nsIAccessibleEvent : nsISupports { /** * An object has been created. */ - const unsigned long EVENT_CREATE = 0x0001; + const unsigned long EVENT_DOM_CREATE = 0x0001; /** * An object has been destroyed. */ - const unsigned long EVENT_DESTROY = 0x0002; + const unsigned long EVENT_DOM_DESTROY = 0x0002; /** - * A hidden object is shown. + * An object's properties or content have changed significantly so that the + * type of object has really changed, and therefore the accessible should be + * destroyed or recreated. */ - const unsigned long EVENT_SHOW = 0x0003; + const unsigned long EVENT_DOM_SIGNIFICANT_CHANGE = 0x0003; /** - * An object is hidden. + * A hidden object is shown -- this is a layout occurance and is thus asynchronous */ - const unsigned long EVENT_HIDE = 0x0004; + const unsigned long EVENT_ASYNCH_SHOW = 0x0004; /** - * A container object has added, removed, or reordered its children. + * An object is hidden -- this is a layout occurance and is thus asynchronous */ - const unsigned long EVENT_REORDER = 0x0005; + const unsigned long EVENT_ASYNCH_HIDE = 0x0005; /** - * An object has a new parent object. + * An object had a significant layout change which could affect + * the type of accessible object -- this is a layout occurance and is thus asynchronous */ - const unsigned long EVENT_PARENT_CHANGE = 0x0006; + const unsigned long EVENT_ASYNCH_SIGNIFICANT_CHANGE = 0x0006; /** * The active descendant of a component has changed. The active descendant @@ -498,6 +501,12 @@ interface nsIAccessibleEvent : nsISupports * May return null if accessible for event has been shut down */ readonly attribute nsIDOMNode DOMNode; + + /** + * Returns true if the event was caused by explicit user input, + * as opposed to purely originating from a timer or mouse movement + */ + readonly attribute boolean isFromUserInput; }; diff --git a/accessible/src/atk/nsAccessibleWrap.cpp b/accessible/src/atk/nsAccessibleWrap.cpp index e8239c89906..e398bc21ca4 100644 --- a/accessible/src/atk/nsAccessibleWrap.cpp +++ b/accessible/src/atk/nsAccessibleWrap.cpp @@ -1120,8 +1120,10 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent) // We don't create ATK objects for nsIAccessible plain text leaves, // just return NS_OK in such case if (!atkObj) { - NS_ASSERTION(type == nsIAccessibleEvent::EVENT_SHOW || - type == nsIAccessibleEvent::EVENT_HIDE, + NS_ASSERTION(type == nsIAccessibleEvent::EVENT_ASYNCH_SHOW || + type == nsIAccessibleEvent::EVENT_ASYNCH_HIDE || + type == nsIAccessibleEvent::EVENT_DOM_CREATE || + type == nsIAccessibleEvent::EVENT_DOM_DESTROY, "Event other than SHOW and HIDE fired for plain text leaves"); return NS_OK; } @@ -1137,7 +1139,8 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent) case nsIAccessibleEvent::EVENT_STATE_CHANGE: return FireAtkStateChangeEvent(aEvent, atkObj); - case nsIAccessibleEvent::EVENT_TEXT_CHANGED: + case nsIAccessibleEvent::EVENT_TEXT_REMOVED: + case nsIAccessibleEvent::EVENT_TEXT_INSERTED: return FireAtkTextChangedEvent(aEvent, atkObj); case nsIAccessibleEvent::EVENT_PROPERTY_CHANGED: @@ -1291,10 +1294,12 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent) *(gint *)eventData); break; - case nsIAccessibleEvent::EVENT_SHOW: + case nsIAccessibleEvent::EVENT_DOM_CREATE: + case nsIAccessibleEvent::EVENT_ASYNCH_SHOW: return FireAtkShowHideEvent(aEvent, atkObj, PR_TRUE); - case nsIAccessibleEvent::EVENT_HIDE: + case nsIAccessibleEvent::EVENT_DOM_DESTROY: + case nsIAccessibleEvent::EVENT_ASYNCH_HIDE: return FireAtkShowHideEvent(aEvent, atkObj, PR_FALSE); /* @@ -1417,7 +1422,7 @@ nsresult nsAccessibleWrap::FireAtkTextChangedEvent(nsIAccessibleEvent *aEvent, AtkObject *aObject) { - MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_CHANGED\n")); + MAI_LOG_DEBUG(("\n\nReceived: EVENT_TEXT_REMOVED/INSERTED\n")); nsCOMPtr event = do_QueryInterface(aEvent); @@ -1516,9 +1521,9 @@ nsAccessibleWrap::FireAtkShowHideEvent(nsIAccessibleEvent *aEvent, AtkObject *aObject, PRBool aIsAdded) { if (aIsAdded) - MAI_LOG_DEBUG(("\n\nReceived: EVENT_SHOW\n")); + MAI_LOG_DEBUG(("\n\nReceived: Show event\n")); else - MAI_LOG_DEBUG(("\n\nReceived: EVENT_HIDE\n")); + MAI_LOG_DEBUG(("\n\nReceived: Hide event\n")); nsCOMPtr accessible; aEvent->GetAccessible(getter_AddRefs(accessible)); diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index e91a5842ebc..5780fab1b14 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -1788,9 +1788,9 @@ NS_IMETHODIMP nsAccessibilityService::InvalidateSubtreeFor(nsIPresShell *aShell, nsIContent *aChangeContent, PRUint32 aEvent) { - NS_ASSERTION(aEvent == nsIAccessibleEvent::EVENT_REORDER || - aEvent == nsIAccessibleEvent::EVENT_SHOW || - aEvent == nsIAccessibleEvent::EVENT_HIDE, + NS_ASSERTION(aEvent == nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE || + aEvent == nsIAccessibleEvent::EVENT_ASYNCH_SHOW || + aEvent == nsIAccessibleEvent::EVENT_ASYNCH_HIDE, "Incorrect aEvent passed in"); nsCOMPtr weakShell(do_GetWeakReference(aShell)); diff --git a/accessible/src/base/nsAccessibilityUtils.cpp b/accessible/src/base/nsAccessibilityUtils.cpp index 9013ee4ef22..59f4b524ac1 100755 --- a/accessible/src/base/nsAccessibilityUtils.cpp +++ b/accessible/src/base/nsAccessibilityUtils.cpp @@ -168,7 +168,8 @@ nsAccUtils::HasListener(nsIContent *aContent, const nsAString& aEventType) } nsresult -nsAccUtils::FireAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible) +nsAccUtils::FireAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible, + PRBool aIsAsynch) { NS_ENSURE_ARG(aAccessible); @@ -176,7 +177,7 @@ nsAccUtils::FireAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible) NS_ASSERTION(pAccessible, "Accessible doesn't implement nsPIAccessible"); nsCOMPtr event = - new nsAccEvent(aEventType, aAccessible, nsnull); + new nsAccEvent(aEventType, aAccessible, nsnull, aIsAsynch); NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY); return pAccessible->FireAccessibleEvent(event); diff --git a/accessible/src/base/nsAccessibilityUtils.h b/accessible/src/base/nsAccessibilityUtils.h index 227de36ca82..901d1ccea46 100755 --- a/accessible/src/base/nsAccessibilityUtils.h +++ b/accessible/src/base/nsAccessibilityUtils.h @@ -111,7 +111,8 @@ public: /** * Fire accessible event of the given type for the given accessible. */ - static nsresult FireAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible); + static nsresult FireAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible, + PRBool aIsAsynch = PR_FALSE); }; #endif diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index 4f718e51b83..1621542f449 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -2083,6 +2083,8 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes) do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID); NS_ENSURE_TRUE(attributes, NS_ERROR_OUT_OF_MEMORY); + nsAccEvent::GetLastEventAttributes(mDOMNode, attributes); + nsresult rv = GetAttributesInternal(attributes); NS_ENSURE_SUCCESS(rv, rv); @@ -2121,17 +2123,13 @@ nsAccessible::GetAttributes(nsIPersistentProperties **aAttributes) // If accessible is invisible we don't want to calculate group ARIA // attributes for it. - PRUint32 state = State(this); - if (state & nsIAccessibleStates::STATE_INVISIBLE) - return NS_OK; - PRUint32 role = Role(this); - if (role == nsIAccessibleRole::ROLE_LISTITEM || + if ((role == nsIAccessibleRole::ROLE_LISTITEM || role == nsIAccessibleRole::ROLE_MENUITEM || role == nsIAccessibleRole::ROLE_RADIOBUTTON || role == nsIAccessibleRole::ROLE_PAGETAB || - role == nsIAccessibleRole::ROLE_OUTLINEITEM) { - + role == nsIAccessibleRole::ROLE_OUTLINEITEM) && + 0 == (State(this) & nsIAccessibleStates::STATE_INVISIBLE)) { nsCOMPtr parent = GetParent(); NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE); @@ -2721,6 +2719,11 @@ NS_IMETHODIMP nsAccessible::GetAccessibleRelated(PRUint32 aRelationType, nsIAcce } break; } + case nsIAccessibleRelation::RELATION_MEMBER_OF: + { + relatedNode = nsAccEvent::GetLastEventAtomicRegion(mDOMNode); + break; + } default: return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/accessible/src/base/nsAccessibleEventData.cpp b/accessible/src/base/nsAccessibleEventData.cpp index d994e22182a..9e8bb7b440d 100755 --- a/accessible/src/base/nsAccessibleEventData.cpp +++ b/accessible/src/base/nsAccessibleEventData.cpp @@ -37,26 +37,184 @@ * ***** END LICENSE BLOCK ***** */ #include "nsAccessibleEventData.h" +#include "nsAccessibilityAtoms.h" #include "nsIAccessibilityService.h" #include "nsIAccessNode.h" +#include "nsIDocument.h" +#include "nsIDOMDocument.h" +#include "nsIEventStateManager.h" +#include "nsIPersistentProperties2.h" #include "nsIServiceManager.h" #ifdef MOZ_XUL #include "nsIDOMXULMultSelectCntrlEl.h" #include "nsXULTreeAccessible.h" #endif +PRBool nsAccEvent::gLastEventFromUserInput = PR_FALSE; +nsIDOMNode* nsAccEvent::gLastEventNodeWeak = 0; + NS_IMPL_ISUPPORTS1(nsAccEvent, nsIAccessibleEvent) nsAccEvent::nsAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible, - void *aEventData): + void *aEventData, PRBool aIsAsynch): mEventType(aEventType), mAccessible(aAccessible), mEventData(aEventData) { + CaptureIsFromUserInput(aIsAsynch); } nsAccEvent::nsAccEvent(PRUint32 aEventType, nsIDOMNode *aDOMNode, - void *aEventData): + void *aEventData, PRBool aIsAsynch): mEventType(aEventType), mDOMNode(aDOMNode), mEventData(aEventData) { + CaptureIsFromUserInput(aIsAsynch); +} + +void nsAccEvent::GetLastEventAttributes(nsIDOMNode *aNode, + nsIPersistentProperties *aAttributes) +{ + if (aNode != gLastEventNodeWeak) { + return; // Passed-in node doesn't Change the last event's node + } + nsAutoString oldValueUnused; + aAttributes->SetStringProperty(NS_LITERAL_CSTRING("event-from-input"), + gLastEventFromUserInput ? NS_LITERAL_STRING("true") : + NS_LITERAL_STRING("false"), + oldValueUnused); + + nsCOMPtr lastEventContent = do_QueryInterface(aNode); + nsIContent *loopContent = lastEventContent; + + nsAutoString atomic, live, relevant, channel, busy; + + while (loopContent) { + if (relevant.IsEmpty()) { + loopContent->GetAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::relevant, relevant); + } + if (live.IsEmpty()) { + loopContent->GetAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::live, live); + } + if (channel.IsEmpty()) { + loopContent->GetAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::channel, channel); + } + if (atomic.IsEmpty()) { + loopContent->GetAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::atomic, atomic); + } + if (busy.IsEmpty()) { + loopContent->GetAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::busy, busy); + } + loopContent = loopContent->GetParent(); + } + + if (!relevant.IsEmpty()) { + aAttributes->SetStringProperty(NS_LITERAL_CSTRING("container-relevant"), relevant, oldValueUnused); + } + if (!live.IsEmpty()) { + aAttributes->SetStringProperty(NS_LITERAL_CSTRING("container-live"), live, oldValueUnused); + } + if (!channel.IsEmpty()) { + aAttributes->SetStringProperty(NS_LITERAL_CSTRING("container-channel"), channel, oldValueUnused); + } + if (!atomic.IsEmpty()) { + aAttributes->SetStringProperty(NS_LITERAL_CSTRING("container-atomic"), atomic, oldValueUnused); + } + if (!busy.IsEmpty()) { + aAttributes->SetStringProperty(NS_LITERAL_CSTRING("container-busy"), busy, oldValueUnused); + } +} + +nsIDOMNode* nsAccEvent::GetLastEventAtomicRegion(nsIDOMNode *aNode) +{ + if (aNode != gLastEventNodeWeak) { + return nsnull; // Passed-in node doesn't Change the last changed node + } + nsCOMPtr lastEventContent = do_QueryInterface(aNode); + nsIContent *loopContent = lastEventContent; + nsAutoString atomic; + + while (loopContent) { + loopContent->GetAttr(kNameSpaceID_WAIProperties, nsAccessibilityAtoms::atomic, atomic); + if (!atomic.IsEmpty()) { + break; + } + loopContent = loopContent->GetParent(); + } + + nsCOMPtr atomicRegion; + if (atomic.EqualsLiteral("true")) { + atomicRegion = do_QueryInterface(loopContent); + } + return atomicRegion; +} + +void nsAccEvent::CaptureIsFromUserInput(PRBool aIsAsynch) +{ + nsCOMPtr eventNode; + GetDOMNode(getter_AddRefs(eventNode)); + if (!eventNode) { + NS_NOTREACHED("There should always be a DOM node for an event"); + return; + } + + if (aIsAsynch) { + // Cannot calculate, so use previous value + gLastEventNodeWeak = eventNode; + } + else { + PrepareForEvent(eventNode); + } + + mIsFromUserInput = gLastEventFromUserInput; +} + +NS_IMETHODIMP +nsAccEvent::GetIsFromUserInput(PRBool *aIsFromUserInput) +{ + *aIsFromUserInput = mIsFromUserInput; + return NS_OK; +} + +void nsAccEvent::PrepareForEvent(nsIAccessibleEvent *aEvent) +{ + nsCOMPtr eventNode; + aEvent->GetDOMNode(getter_AddRefs(eventNode)); + PRBool isFromUserInput; + aEvent->GetIsFromUserInput(&isFromUserInput); + PrepareForEvent(eventNode, isFromUserInput); +} + +void nsAccEvent::PrepareForEvent(nsIDOMNode *aEventNode, + PRBool aForceIsFromUserInput) +{ + gLastEventNodeWeak = aEventNode; + if (aForceIsFromUserInput) { + gLastEventFromUserInput = PR_TRUE; + return; + } + + nsCOMPtr domDoc; + aEventNode->GetOwnerDocument(getter_AddRefs(domDoc)); + if (!domDoc) { // IF the node is a document itself + domDoc = do_QueryInterface(aEventNode); + } + nsCOMPtr doc = do_QueryInterface(domDoc); + if (!doc) { + NS_NOTREACHED("There should always be a document for an event"); + return; + } + + nsCOMPtr presShell = doc->GetPrimaryShell(); + if (!presShell) { + NS_NOTREACHED("Threre should always be an pres shell for an event"); + return; + } + + nsIEventStateManager *esm = presShell->GetPresContext()->EventStateManager(); + if (!esm) { + NS_NOTREACHED("Threre should always be an ESM for an event"); + return; + } + + gLastEventFromUserInput = esm->IsHandlingUserInputExternal(); } NS_IMETHODIMP @@ -234,7 +392,8 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsAccTextChangeEvent, nsAccEvent, nsAccTextChangeEvent:: nsAccTextChangeEvent(nsIAccessible *aAccessible, PRInt32 aStart, PRUint32 aLength, PRBool aIsInserted): - nsAccEvent(::nsIAccessibleEvent::EVENT_TEXT_CHANGED, aAccessible, nsnull), + nsAccEvent(aIsInserted ? nsIAccessibleEvent::EVENT_TEXT_INSERTED : nsIAccessibleEvent::EVENT_TEXT_REMOVED, + aAccessible, nsnull), mStart(aStart), mLength(aLength), mIsInserted(aIsInserted) { } @@ -266,14 +425,14 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsAccCaretMoveEvent, nsAccEvent, nsAccCaretMoveEvent:: nsAccCaretMoveEvent(nsIAccessible *aAccessible, PRInt32 aCaretOffset) : - nsAccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible, nsnull), + nsAccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible, nsnull, PR_TRUE), // Currently always asynch mCaretOffset(aCaretOffset) { } nsAccCaretMoveEvent:: nsAccCaretMoveEvent(nsIDOMNode *aNode) : - nsAccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aNode, nsnull), + nsAccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aNode, nsnull, PR_TRUE), // Currently always asynch mCaretOffset(-1) { } diff --git a/accessible/src/base/nsAccessibleEventData.h b/accessible/src/base/nsAccessibleEventData.h index 2cec7b76acc..e79996d03a4 100644 --- a/accessible/src/base/nsAccessibleEventData.h +++ b/accessible/src/base/nsAccessibleEventData.h @@ -46,29 +46,56 @@ #include "nsIAccessible.h" #include "nsIAccessibleDocument.h" #include "nsIDOMNode.h" +class nsIPresShell; class nsAccEvent: public nsIAccessibleEvent { public: // Initialize with an nsIAccessible - nsAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible, void *aEventData); + nsAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible, void *aEventData, PRBool aIsAsynch = PR_FALSE); // Initialize with an nsIDOMNode - nsAccEvent(PRUint32 aEventType, nsIDOMNode *aDOMNode, void *aEventData); + nsAccEvent(PRUint32 aEventType, nsIDOMNode *aDOMNode, void *aEventData, PRBool aIsAsynch = PR_FALSE); virtual ~nsAccEvent() {} NS_DECL_ISUPPORTS NS_DECL_NSIACCESSIBLEEVENT + static void GetLastEventAttributes(nsIDOMNode *aNode, + nsIPersistentProperties *aAttributes); + static nsIDOMNode* nsAccEvent::GetLastEventAtomicRegion(nsIDOMNode *aNode); + protected: already_AddRefed GetAccessibleByNode(); + void CaptureIsFromUserInput(PRBool aIsAsynch); + PRBool mIsFromUserInput; + private: PRUint32 mEventType; nsCOMPtr mAccessible; nsCOMPtr mDOMNode; nsCOMPtr mDocAccessible; + static PRBool gLastEventFromUserInput; + static nsIDOMNode* gLastEventNodeWeak; + public: + /** + * Find and cache the last input state. This will be called automatically + * for synchronous events. For asynchronous events it should be + * called from the synchronous code which is the true source of the event, + * before the event is fired. + */ + static void PrepareForEvent(nsIDOMNode *aChangeNode, + PRBool aForceIsFromUserInput = PR_FALSE); + + /** + * The input state was previously stored with the nsIAccessibleEvent, + * so use that state now -- call this when about to flush an event that + * was waiting in an event queue + */ + static void PrepareForEvent(nsIAccessibleEvent *aEvent); + void *mEventData; }; diff --git a/accessible/src/base/nsDocAccessible.cpp b/accessible/src/base/nsDocAccessible.cpp index 032b497441a..64c7ebcd68c 100644 --- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -950,6 +950,11 @@ nsDocAccessible::AttributeChanged(nsIDocument *aDocument, nsIContent* aContent, return; } + // Since we're in synchronous code, we can store whether the current attribute + // change is from user input or not. If the attribute change causes an asynchronous + // layout change, that event can use the last known user input state + nsAccEvent::PrepareForEvent(targetNode); + // Universal boolean properties that don't require a role. if (aAttribute == nsAccessibilityAtoms::disabled) { // Fire the state change whether disabled attribute is @@ -979,14 +984,14 @@ nsDocAccessible::AttributeChanged(nsIDocument *aDocument, nsIContent* aContent, if (aNameSpaceID == kNameSpaceID_XHTML2_Unofficial || aNameSpaceID == kNameSpaceID_XHTML) { if (aAttribute == nsAccessibilityAtoms::role) - InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_REORDER); + InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE); return; } if (aAttribute == nsAccessibilityAtoms::href || aAttribute == nsAccessibilityAtoms::onclick || aAttribute == nsAccessibilityAtoms::droppable) { - InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_REORDER); + InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE); return; } @@ -1112,7 +1117,7 @@ nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute) if (HasRoleAttribute(aContent)) { // The multiselectable and other waistate attributes only take affect // when dynamic content role is present - InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_REORDER); + InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE); } } } @@ -1139,7 +1144,7 @@ void nsDocAccessible::ContentAppended(nsIDocument *aDocument, // 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(child, nsIAccessibleEvent::EVENT_SHOW); + InvalidateCacheSubtree(child, nsIAccessibleEvent::EVENT_DOM_CREATE); } } @@ -1173,7 +1178,7 @@ nsDocAccessible::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer, // 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, nsIAccessibleEvent::EVENT_SHOW); + InvalidateCacheSubtree(aChild, nsIAccessibleEvent::EVENT_DOM_CREATE); } void @@ -1183,7 +1188,7 @@ nsDocAccessible::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer, FireTextChangedEventOnDOMNodeRemoved(aChild, aContainer, aIndexInContainer); // Invalidate the subtree of the removed element. - InvalidateCacheSubtree(aChild, nsIAccessibleEvent::EVENT_HIDE); + InvalidateCacheSubtree(aChild, nsIAccessibleEvent::EVENT_DOM_DESTROY); } void @@ -1364,17 +1369,20 @@ nsDocAccessible::FireTextChangedEventOnDOMNodeRemoved(nsIContent *aChild, nsresult nsDocAccessible::FireDelayedToolkitEvent(PRUint32 aEvent, nsIDOMNode *aDOMNode, void *aData, - PRBool aAllowDupes) + PRBool aAllowDupes, + PRBool aIsAsynch) { - nsCOMPtr event = new nsAccEvent(aEvent, aDOMNode, aData); + nsCOMPtr event = + new nsAccEvent(aEvent, aDOMNode, aData, PR_TRUE); NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY); - return FireDelayedAccessibleEvent(event); + return FireDelayedAccessibleEvent(event, aAllowDupes, aIsAsynch); } nsresult nsDocAccessible::FireDelayedAccessibleEvent(nsIAccessibleEvent *aEvent, - PRBool aAllowDupes) + PRBool aAllowDupes, + PRBool aIsAsynch) { PRBool isTimerStarted = PR_TRUE; PRInt32 numQueuedEvents = mEventsToFire.Count(); @@ -1390,6 +1398,13 @@ nsDocAccessible::FireDelayedAccessibleEvent(nsIAccessibleEvent *aEvent, nsCOMPtr newEventDOMNode; aEvent->GetDOMNode(getter_AddRefs(newEventDOMNode)); + if (!aIsAsynch) { + // If already asynchronous don't call PrepareFromEvent() -- it + // should only be called while ESM still knows if the event occurred + // originally because of user input + nsAccEvent::PrepareForEvent(newEventDOMNode); + } + if (numQueuedEvents == 0) { isTimerStarted = PR_FALSE; } else if (!aAllowDupes) { @@ -1470,11 +1485,14 @@ NS_IMETHODIMP nsDocAccessible::FlushPendingEvents() accessibleText->GetSelectionCount(&selectionCount); if (selectionCount) { // There's a selection so fire selection change as well nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, - accessible); + accessible, PR_TRUE); } } } else { + // The input state was previously stored with the nsIAccessibleEvent, + // so use that state now when firing the event + nsAccEvent::PrepareForEvent(accessibleEvent); FireAccessibleEvent(accessibleEvent); } } @@ -1490,11 +1508,8 @@ void nsDocAccessible::FlushEventsCallback(nsITimer *aTimer, void *aClosure) accessibleDoc->FlushPendingEvents(); } -void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode, PRUint32 aChangeEvent) +void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode) { - NS_ASSERTION(aChangeEvent != nsIAccessibleEvent::EVENT_SHOW, - "nsDocAccessible::RefreshNodes isn't supposed to work with show event."); - nsCOMPtr iterNode(aStartNode), nextNode; nsCOMPtr accessNode; @@ -1565,11 +1580,26 @@ void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode, PRUint32 aChangeEvent NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild, PRUint32 aChangeEventType) { - NS_ASSERTION(aChangeEventType == nsIAccessibleEvent::EVENT_REORDER || - aChangeEventType == nsIAccessibleEvent::EVENT_SHOW || - aChangeEventType == nsIAccessibleEvent::EVENT_HIDE, + PRBool isHiding = + aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE || + aChangeEventType == nsIAccessibleEvent::EVENT_DOM_DESTROY; + + PRBool isShowing = + aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW || + aChangeEventType == nsIAccessibleEvent::EVENT_DOM_CREATE; + + PRBool isChanging = + aChangeEventType == nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE || + aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE; + + NS_ASSERTION(isChanging || isHiding || isShowing, "Incorrect aChangeEventType passed in"); + PRBool isAsynch = + aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE || + aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW || + aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE; + // Invalidate cache subtree // We have to check for accessibles for each dom node by traversing DOM tree // instead of just the accessible tree, although that would be faster @@ -1591,10 +1621,11 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild, return InvalidateChildren(); } + // Update last change state information nsCOMPtr childAccessNode; GetCachedAccessNode(childNode, getter_AddRefs(childAccessNode)); nsCOMPtr childAccessible = do_QueryInterface(childAccessNode); - if (!childAccessible && aChangeEventType != nsIAccessibleEvent::EVENT_HIDE) { + if (!childAccessible && isHiding) { // If not about to hide it, make sure there's an accessible so we can fire an // event for it GetAccService()->GetAccessibleFor(childNode, getter_AddRefs(childAccessible)); @@ -1604,28 +1635,35 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild, nsAutoString localName; childNode->GetLocalName(localName); const char *hasAccessible = childAccessible ? " (acc)" : ""; - if (aChangeEventType == nsIAccessibleEvent::EVENT_HIDE) { + if (aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE) { printf("[Hide %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible); } - else if (aChangeEventType == nsIAccessibleEvent::EVENT_SHOW) { + else if (aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW) { printf("[Show %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible); } - else if (aChangeEventType == nsIAccessibleEvent::EVENT_REORDER) { - printf("[Reorder %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible); + else if (aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE) { + printf("[Layout change %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible); + } + else if (aChangeEventType == nsIAccessibleEvent::EVENT_DOM_CREATE) { + printf("[Create %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible); + } + else if (aChangeEventType == nsIAccessibleEvent::EVENT_DOM_DESTROY) { + printf("[Destroy %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible); + } + else if (aChangeEventType == nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE) { + printf("[Type change %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible); } #endif - if (aChangeEventType == nsIAccessibleEvent::EVENT_HIDE || - aChangeEventType == nsIAccessibleEvent::EVENT_REORDER) { - // Fire EVENT_HIDE if previous accessible existed for node being hidden. + if (!isShowing) { + // Fire EVENT_HIDE or EVENT_DOM_DESTROY if previous accessible existed for node being hidden. // Fire this before the accessible goes away. - if (childAccessible) - nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_HIDE, childAccessible); - } - - // Shutdown nsIAccessNode's or nsIAccessibles for any DOM nodes in this subtree - if (aChangeEventType != nsIAccessibleEvent::EVENT_SHOW) { - RefreshNodes(childNode, aChangeEventType); + if (childAccessible) { + PRUint32 removalEvent = isAsynch ? nsIAccessibleEvent::EVENT_ASYNCH_HIDE : nsIAccessibleEvent::EVENT_DOM_DESTROY; + nsAccUtils::FireAccEvent(removalEvent, childAccessible, isAsynch); + } + // Shutdown nsIAccessNode's or nsIAccessibles for any DOM nodes in this subtree + RefreshNodes(childNode); } // We need to get an accessible for the mutation event's container node @@ -1646,46 +1684,35 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild, privateContainerAccessible->InvalidateChildren(); } - // Fire an event so the assistive technology knows the objects it is holding onto - // in this part of the subtree are no longer useful and should be released. - // However, they still won't crash if the AT tries to use them, because a stub of the - // object still exists as long as it is refcounted, even from outside of Gecko. - nsCOMPtr containerAccessNode = - do_QueryInterface(containerAccessible); - if (containerAccessNode) { - nsCOMPtr containerNode; - containerAccessNode->GetDOMNode(getter_AddRefs(containerNode)); - if (containerNode) { - FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_REORDER, - containerNode, nsnull); - } - } - - if (aChild && (aChangeEventType == nsIAccessibleEvent::EVENT_SHOW || - aChangeEventType == nsIAccessibleEvent::EVENT_REORDER)) { + if (aChild && !isHiding) { // Fire EVENT_SHOW, EVENT_MENUPOPUP_START for newly visible content. // Fire after a short timer, because we want to make sure the view has been // updated to make this accessible content visible. If we don't wait, // the assistive technology may receive the event and then retrieve // nsIAccessibleStates::STATE_INVISIBLE for the event's accessible object. - FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_SHOW, childNode, nsnull); + PRUint32 additionEvent = isAsynch ? nsIAccessibleEvent::EVENT_ASYNCH_SHOW : + nsIAccessibleEvent::EVENT_DOM_CREATE; + if (!isAsynch) { + // Calculate "is from user input" while we still synchronous and have the info + nsAccEvent::PrepareForEvent(childNode); + } + FireDelayedToolkitEvent(additionEvent, childNode, nsnull, PR_TRUE, isAsynch); + + // Check to see change occured in an ARIA menu, and fire an EVENT_MENUPOPUP_START if it did nsAutoString role; if (GetRoleAttribute(aChild, role) && StringEndsWith(role, NS_LITERAL_STRING(":menu"), nsCaseInsensitiveStringComparator())) { FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START, - childNode, nsnull); + childNode, nsnull, PR_TRUE, isAsynch); } - } - // Check to see if change occured inside an alert, and fire an EVENT_ALERT if it did - if (aChangeEventType != nsIAccessibleEvent::EVENT_HIDE) { + // Check to see if change occured inside an alert, and fire an EVENT_ALERT if it did nsIContent *ancestor = aChild; - nsAutoString role; while (ancestor) { if (GetRoleAttribute(ancestor, role) && StringEndsWith(role, NS_LITERAL_STRING(":alert"), nsCaseInsensitiveStringComparator())) { nsCOMPtr alertNode(do_QueryInterface(ancestor)); - FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_ALERT, alertNode, nsnull); + FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_ALERT, alertNode, nsnull, PR_FALSE, isAsynch); break; } ancestor = ancestor->GetParent(); @@ -1755,7 +1782,7 @@ void nsDocAccessible::DocLoadCallback(nsITimer *aTimer, void *aClosure) docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); if (sameTypeRoot != docShellTreeItem) { // A frame or iframe has finished loading new content - docAcc->InvalidateCacheSubtree(nsnull, nsIAccessibleEvent::EVENT_REORDER); + docAcc->InvalidateCacheSubtree(nsnull, nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE); return; } diff --git a/accessible/src/base/nsDocAccessible.h b/accessible/src/base/nsDocAccessible.h index 91f68733a77..c5f4c2bc63c 100644 --- a/accessible/src/base/nsDocAccessible.h +++ b/accessible/src/base/nsDocAccessible.h @@ -99,19 +99,35 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap, // nsPIAccessNode NS_IMETHOD_(nsIFrame *) GetFrame(void); - // Non-virtual + /** + * Non-virtual method to fire a delayed event after a 0 length timeout + * + * @param aEvent - the nsIAccessibleEvent event ype + * @param aDOMNode - DOM node the accesible event should be fired for + * @param aData - any additional data for the event + * @param aAllowDupes - set to PR_TRUE if more than one event of the same + * type is allowed. By default this is false and events + * of the same type are discarded (the last one is used) + * @param aIsAsyn - set to PR_TRUE if this is not being called from code + * synchronous with a DOM event + */ nsresult FireDelayedToolkitEvent(PRUint32 aEvent, nsIDOMNode *aDOMNode, - void *aData, PRBool aAllowDupes = PR_FALSE); + void *aData, PRBool aAllowDupes = PR_FALSE, + PRBool aIsAsynch = PR_FALSE); /** * Fire accessible event in timeout. * + * @param aEvent - the event to fire * @param aAllowDupes - if false then delayed events of the same type and * for the same DOM node in the event queue won't * be fired. + * @param aIsAsych - set to PR_TRUE if this is being called from + * an event asynchronous with the DOM */ nsresult FireDelayedAccessibleEvent(nsIAccessibleEvent *aEvent, - PRBool aAllowDupes = PR_FALSE); + PRBool aAllowDupes = PR_FALSE, + PRBool aIsAsynch = PR_FALSE); void ShutdownChildDocuments(nsIDocShellTreeItem *aStart); @@ -121,7 +137,7 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap, virtual nsresult RemoveEventListeners(); void AddScrollListener(); void RemoveScrollListener(); - void RefreshNodes(nsIDOMNode *aStartNode, PRUint32 aChangeEvent); + void RefreshNodes(nsIDOMNode *aStartNode); static void ScrollTimerCallback(nsITimer *aTimer, void *aClosure); void CheckForEditor(); virtual void SetEditor(nsIEditor *aEditor); diff --git a/accessible/src/base/nsRootAccessible.cpp b/accessible/src/base/nsRootAccessible.cpp index 8501c1ea0fd..516705669e0 100644 --- a/accessible/src/base/nsRootAccessible.cpp +++ b/accessible/src/base/nsRootAccessible.cpp @@ -425,7 +425,8 @@ void nsRootAccessible::TryFireEarlyLoadEvent(nsIDOMNode *aDocNode) PRBool nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible, nsIDOMNode *aNode, nsIDOMEvent *aFocusEvent, - PRBool aForceEvent) + PRBool aForceEvent, + PRBool aIsAsynch) { if (mCaretAccessible) { nsCOMPtr nsevent(do_QueryInterface(aFocusEvent)); @@ -510,7 +511,7 @@ PRBool nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible, } FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_FOCUS, - finalFocusNode, nsnull); + finalFocusNode, nsnull, PR_FALSE, aIsAsynch); return PR_TRUE; } @@ -607,12 +608,12 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent, return NS_OK; } - if (eventType.EqualsLiteral("TreeViewChanged")) { + if (eventType.EqualsLiteral("TreeViewChanged")) { // Always asynch, always from user input NS_ENSURE_TRUE(localName.EqualsLiteral("tree"), NS_OK); nsCOMPtr treeContent = do_QueryInterface(aTargetNode); - + nsAccEvent::PrepareForEvent(aTargetNode, PR_TRUE); return accService->InvalidateSubtreeFor(eventShell, treeContent, - nsIAccessibleEvent::EVENT_REORDER); + nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE); } nsCOMPtr accessible; @@ -764,7 +765,7 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent, // The accessible for it stays the same no matter where it moves. // AT's expect to get an EVENT_SHOW for the tooltip. // In event callback the tooltip's accessible will be ready. - event = nsIAccessibleEvent::EVENT_SHOW; + event = nsIAccessibleEvent::EVENT_ASYNCH_SHOW; } if (event) { nsAccUtils::FireAccEvent(event, accessible); @@ -822,13 +823,16 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent, return NS_OK; } } - FireAccessibleFocusEvent(accessible, aTargetNode, aEvent, PR_TRUE); + nsAccEvent::PrepareForEvent(aTargetNode, PR_TRUE); // Always asynch, always from user input + FireAccessibleFocusEvent(accessible, aTargetNode, aEvent, PR_TRUE, PR_TRUE); } - else if (eventType.EqualsLiteral("DOMMenuBarActive")) { - nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENU_START, accessible); + else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always asynch, always from user input + nsAccEvent::PrepareForEvent(aTargetNode, PR_TRUE); + nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENU_START, accessible, PR_TRUE); } - else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { - nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENU_END, accessible); + else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always asynch, always from user input + nsAccEvent::PrepareForEvent(aTargetNode, PR_TRUE); + nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENU_END, accessible, PR_TRUE); FireCurrentFocusEvent(); } else if (eventType.EqualsLiteral("ValueChange")) { diff --git a/accessible/src/base/nsRootAccessible.h b/accessible/src/base/nsRootAccessible.h index caf67737d58..3a89ddcb04a 100644 --- a/accessible/src/base/nsRootAccessible.h +++ b/accessible/src/base/nsRootAccessible.h @@ -101,7 +101,8 @@ class nsRootAccessible : public nsDocAccessibleWrap, PRBool FireAccessibleFocusEvent(nsIAccessible *aFocusAccessible, nsIDOMNode *aFocusNode, nsIDOMEvent *aFocusEvent, - PRBool aForceEvent = PR_FALSE); + PRBool aForceEvent = PR_FALSE, + PRBool aIsAsynch = PR_FALSE); nsCaretAccessible *GetCaretAccessible(); diff --git a/accessible/src/html/nsHyperTextAccessible.cpp b/accessible/src/html/nsHyperTextAccessible.cpp index 809060b69a4..966ee0a3c8e 100644 --- a/accessible/src/html/nsHyperTextAccessible.cpp +++ b/accessible/src/html/nsHyperTextAccessible.cpp @@ -520,12 +520,17 @@ nsresult nsHyperTextAccessible::DOMPointToHypertextOffset(nsIDOMNode* aNode, PRI NS_ENSURE_TRUE(parentContent, NS_ERROR_FAILURE); // findNode could be null if aNodeOffset == # of child nodes, which means one of two things: // 1) we're at the end of the children, keep findNode = null, so that we get the last possible offset - // 2) there are no children, use parentContent for the node to find. In this case parentContent can't be - // the nsIAccessibleText, because an accesible text must have children + // 2) there are no children and the passed-in node is mDOMNode, which means we're an aempty nsIAccessibleText + // 3) there are no children, and the passed-in node is not mDOMNode -- use parentContent for the node to find findNode = do_QueryInterface(parentContent->GetChildAt(aNodeOffset)); if (!findNode && !aNodeOffset) { - NS_ASSERTION(!SameCOMIdentity(parentContent, mDOMNode), "Cannot find child for DOMPointToHypertextOffset search"); + if (SameCOMIdentity(parentContent, mDOMNode)) { + // There are no children, which means this is an empty nsIAccessibleText, in which + // case we can only be at hypertext offset 0 + *aHyperTextOffset = 0; + return NS_OK; + } findNode = do_QueryInterface(parentContent); // Case #2: there are no children } } diff --git a/accessible/src/msaa/nsAccessibleWrap.cpp b/accessible/src/msaa/nsAccessibleWrap.cpp index dc656a69da4..c7ec0ba4a8f 100644 --- a/accessible/src/msaa/nsAccessibleWrap.cpp +++ b/accessible/src/msaa/nsAccessibleWrap.cpp @@ -1529,7 +1529,8 @@ nsAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent) // See if we're in a scrollable area with its own window nsCOMPtr newAccessible; - if (eventType == nsIAccessibleEvent::EVENT_HIDE) { + if (eventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE || + eventType == nsIAccessibleEvent::EVENT_DOM_DESTROY) { // Don't use frame from current accessible when we're hiding that // accessible. accessible->GetParent(getter_AddRefs(newAccessible)); diff --git a/accessible/src/msaa/nsEventMap.h b/accessible/src/msaa/nsEventMap.h index a6b82a6b2c7..3fd8ce93a52 100755 --- a/accessible/src/msaa/nsEventMap.h +++ b/accessible/src/msaa/nsEventMap.h @@ -46,12 +46,12 @@ const PRUint32 kEVENT_LAST_ENTRY = 0xffffffff; static const PRUint32 gWinEventMap[] = { kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent doesn't have 0 constant - kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_CREATE - kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_DESTROY - EVENT_OBJECT_SHOW, // nsIAccessibleEvent::EVENT_SHOW - EVENT_OBJECT_HIDE, // nsIAccessibleEvent::EVENT_HIDE - EVENT_OBJECT_REORDER, // nsIAccessibleEvent::EVENT_REORDER - kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_PARENT_CHANGE + EVENT_OBJECT_SHOW, // nsIAccessibleEvent::EVENT_DOM_CREATE + EVENT_OBJECT_HIDE, // nsIAccessibleEvent::EVENT_DOM_DESTROY + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE + EVENT_OBJECT_SHOW, // nsIAccessibleEvent::EVENT_ASYNCH_SHOW + EVENT_OBJECT_HIDE, // nsIAccessibleEvent::EVENT_ASYNCH_HIDE + kEVENT_WIN_UNKNOWN, // nsIAccessibleEvent::EVENT_ASYNCH_LAYOUT_CHANGE IA2_EVENT_ACTIVE_DECENDENT_CHANGED, // nsIAccessibleEvent::EVENT_ACTIVE_DECENDENT_CHANGED EVENT_OBJECT_FOCUS, // nsIAccessibleEvent::EVENT_FOCUS EVENT_OBJECT_STATECHANGE, // nsIAccessibleEvent::EVENT_STATE_CHANGE diff --git a/accessible/src/msaa/nsHyperTextAccessibleWrap.cpp b/accessible/src/msaa/nsHyperTextAccessibleWrap.cpp index b800cf549c6..533fb9e026b 100755 --- a/accessible/src/msaa/nsHyperTextAccessibleWrap.cpp +++ b/accessible/src/msaa/nsHyperTextAccessibleWrap.cpp @@ -54,7 +54,8 @@ nsHyperTextAccessibleWrap::FireAccessibleEvent(nsIAccessibleEvent *aEvent) PRUint32 eventType; aEvent->GetEventType(&eventType); - if (eventType == nsIAccessibleEvent::EVENT_TEXT_CHANGED) { + if (eventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED || + eventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED) { nsCOMPtr accessible; aEvent->GetAccessible(getter_AddRefs(accessible)); if (accessible) { diff --git a/content/events/public/nsIEventStateManager.h b/content/events/public/nsIEventStateManager.h index 793802074af..5425fc1082e 100644 --- a/content/events/public/nsIEventStateManager.h +++ b/content/events/public/nsIEventStateManager.h @@ -52,12 +52,10 @@ class imgIContainer; /* * Event state manager interface. */ -// {9d25327a-7a17-4d19-928c-f7f3ac19b763} +// {fb7516ff-2f01-4893-84e8-e4b282813023} #define NS_IEVENTSTATEMANAGER_IID \ -{ 0x9d25327a, 0x7a17, 0x4d19, \ - { 0x92, 0x8c, 0xf7, 0xf3, 0xac, 0x19, 0xb7, 0x63 } } - - +{ 0xfb7516ff, 0x2f01, 0x4893, \ + { 0x84, 0xe8, 0xe4, 0xb2, 0x82, 0x81, 0x30, 0x23 } }; #define NS_EVENT_NEEDS_FRAME(event) (!NS_IS_FOCUS_EVENT(event)) @@ -145,6 +143,15 @@ public: NS_IMETHOD ShiftFocus(PRBool aDirection, nsIContent* aStart)=0; NS_IMETHOD NotifyDestroyPresContext(nsPresContext* aPresContext) = 0; + + /** + * Returns true if the current code is being executed as a result of user input. + * This includes timers or anything else that is initiated from user input. + * However, mouse hover events are not counted as user input, nor are + * page load events. If this method is called from asynchronously executed code, + * such as during layout reflows, it will return false. + */ + NS_IMETHOD_(PRBool) IsHandlingUserInputExternal() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIEventStateManager, NS_IEVENTSTATEMANAGER_IID) diff --git a/content/events/src/nsEventStateManager.h b/content/events/src/nsEventStateManager.h index a1c0a6592ff..b87e723ceea 100644 --- a/content/events/src/nsEventStateManager.h +++ b/content/events/src/nsEventStateManager.h @@ -156,6 +156,8 @@ public: return sUserInputEventDepth > 0; } + NS_IMETHOD_(PRBool) IsHandlingUserInputExternal() { return IsHandlingUserInput(); } + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsEventStateManager, nsIEventStateManager) diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 6c5424aab8c..92ce13e338d 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -11133,10 +11133,11 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent) PRUint32 event; if (frame) { nsIFrame *newFrame = mPresShell->GetPrimaryFrameFor(aContent); - event = newFrame ? nsIAccessibleEvent::EVENT_REORDER : nsIAccessibleEvent::EVENT_HIDE; + event = newFrame ? nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE : + nsIAccessibleEvent::EVENT_ASYNCH_HIDE; } else { - event = nsIAccessibleEvent::EVENT_SHOW; + event = nsIAccessibleEvent::EVENT_ASYNCH_SHOW; } // A significant enough change occured that this part diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index c1e0a2e870a..fc3b1d542c0 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -1381,8 +1381,8 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, do_GetService("@mozilla.org/accessibilityService;1"); if (accService) { accService->InvalidateSubtreeFor(mPresShell, aFrame->GetContent(), - isVisible ? nsIAccessibleEvent::EVENT_HIDE : - nsIAccessibleEvent::EVENT_SHOW); + isVisible ? nsIAccessibleEvent::EVENT_ASYNCH_HIDE : + nsIAccessibleEvent::EVENT_ASYNCH_SHOW); } } #endif diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index a9339683401..78a0d542ae3 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -3271,7 +3271,7 @@ void nsIPresShell::InvalidateAccessibleSubtree(nsIContent *aContent) do_GetService("@mozilla.org/accessibilityService;1"); if (accService) { accService->InvalidateSubtreeFor(this, aContent, - nsIAccessibleEvent::EVENT_REORDER); + nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE); } } }