зеркало из https://github.com/mozilla/gecko-dev.git
Bug 391847. Coalesce accessible mutation events for the same subtree. r=ginn.chen, sr=bz, a=bz
This commit is contained in:
Родитель
bc19123a51
Коммит
104a194eae
|
@ -183,3 +183,22 @@ nsAccUtils::FireAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible,
|
|||
return pAccessible->FireAccessibleEvent(event);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsAccUtils::IsAncestorOf(nsIDOMNode *aPossibleAncestorNode,
|
||||
nsIDOMNode *aPossibleDescendantNode)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aPossibleAncestorNode);
|
||||
NS_ENSURE_ARG_POINTER(aPossibleDescendantNode);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> loopNode = aPossibleDescendantNode;
|
||||
nsCOMPtr<nsIDOMNode> parentNode;
|
||||
while (NS_SUCCEEDED(loopNode->GetParentNode(getter_AddRefs(parentNode))) &&
|
||||
parentNode) {
|
||||
if (parentNode == aPossibleAncestorNode) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
loopNode.swap(parentNode);
|
||||
}
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
|
|
|
@ -113,6 +113,16 @@ public:
|
|||
*/
|
||||
static nsresult FireAccEvent(PRUint32 aEventType, nsIAccessible *aAccessible,
|
||||
PRBool aIsAsynch = PR_FALSE);
|
||||
|
||||
/**
|
||||
* Is the first passed in node an ancestor of the second?
|
||||
* Note: A node is not considered to be the ancestor of itself.
|
||||
* @aPossibleAncestorNode -- node to test for ancestor-ness of aPossibleDescendantNode
|
||||
* @aPossibleDescendantNode -- node to test for descendant-ness of aPossibleAncestorNode
|
||||
* @return PR_TRUE if aPossibleAncestorNode is an ancestor of aPossibleDescendantNode
|
||||
*/
|
||||
static PRBool IsAncestorOf(nsIDOMNode *aPossibleAncestorNode,
|
||||
nsIDOMNode *aPossibleDescendantNode);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -221,7 +221,7 @@ NS_IMETHODIMP nsCaretAccessible::NotifySelectionChanged(nsIDOMDocument *aDoc, ns
|
|||
new nsAccCaretMoveEvent(focusNode);
|
||||
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return mRootAccessible->FireDelayedAccessibleEvent(event, PR_FALSE);
|
||||
return mRootAccessible->FireDelayedAccessibleEvent(event, nsDocAccessible::eRemoveDupes);
|
||||
}
|
||||
|
||||
nsRect
|
||||
|
|
|
@ -1006,7 +1006,7 @@ nsDocAccessible::AttributeChanged(nsIDocument *aDocument, nsIContent* aContent,
|
|||
multiSelectAccessNode->GetDOMNode(getter_AddRefs(multiSelectDOMNode));
|
||||
NS_ASSERTION(multiSelectDOMNode, "A new accessible without a DOM node!");
|
||||
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
|
||||
multiSelectDOMNode, nsnull, PR_TRUE);
|
||||
multiSelectDOMNode, nsnull, eAllowDupes);
|
||||
|
||||
static nsIContent::AttrValuesArray strings[] =
|
||||
{&nsAccessibilityAtoms::_empty, &nsAccessibilityAtoms::_false, nsnull};
|
||||
|
@ -1381,7 +1381,7 @@ nsDocAccessible::FireTextChangedEventOnDOMNodeRemoved(nsIContent *aChild,
|
|||
nsresult nsDocAccessible::FireDelayedToolkitEvent(PRUint32 aEvent,
|
||||
nsIDOMNode *aDOMNode,
|
||||
void *aData,
|
||||
PRBool aAllowDupes,
|
||||
EDupeEventRule aAllowDupes,
|
||||
PRBool aIsAsynch)
|
||||
{
|
||||
nsCOMPtr<nsIAccessibleEvent> event =
|
||||
|
@ -1393,8 +1393,8 @@ nsresult nsDocAccessible::FireDelayedToolkitEvent(PRUint32 aEvent,
|
|||
|
||||
nsresult
|
||||
nsDocAccessible::FireDelayedAccessibleEvent(nsIAccessibleEvent *aEvent,
|
||||
PRBool aAllowDupes,
|
||||
PRBool aIsAsynch)
|
||||
EDupeEventRule aAllowDupes,
|
||||
PRBool aIsAsynch)
|
||||
{
|
||||
PRBool isTimerStarted = PR_TRUE;
|
||||
PRInt32 numQueuedEvents = mEventsToFire.Count();
|
||||
|
@ -1419,7 +1419,36 @@ nsDocAccessible::FireDelayedAccessibleEvent(nsIAccessibleEvent *aEvent,
|
|||
|
||||
if (numQueuedEvents == 0) {
|
||||
isTimerStarted = PR_FALSE;
|
||||
} else if (!aAllowDupes) {
|
||||
} else if (aAllowDupes == eCoalesceFromSameSubtree) {
|
||||
// Especially for mutation events, we will define a duplicate event
|
||||
// as one on the same node or on a descendant node.
|
||||
// This prevents a flood of events when a subtree is changed.
|
||||
for (PRInt32 index = 0; index < numQueuedEvents; index ++) {
|
||||
nsIAccessibleEvent *accessibleEvent = mEventsToFire[index];
|
||||
NS_ASSERTION(accessibleEvent, "Array item is not an accessible event");
|
||||
if (!accessibleEvent) {
|
||||
continue;
|
||||
}
|
||||
PRUint32 eventType;
|
||||
accessibleEvent->GetEventType(&eventType);
|
||||
if (eventType == newEventType) {
|
||||
nsCOMPtr<nsIDOMNode> domNode;
|
||||
accessibleEvent->GetDOMNode(getter_AddRefs(domNode));
|
||||
if (newEventDOMNode == domNode || nsAccUtils::IsAncestorOf(newEventDOMNode, domNode)) {
|
||||
mEventsToFire.RemoveObjectAt(index);
|
||||
// The other event is the same type, but in a descendant of this
|
||||
// event, so remove that one. The umbrella event in the ancestor
|
||||
// is already enough
|
||||
-- index;
|
||||
-- numQueuedEvents;
|
||||
}
|
||||
else if (nsAccUtils::IsAncestorOf(domNode, newEventDOMNode)) {
|
||||
// There is a better SHOW/HIDE event (it's in an ancestor)
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (aAllowDupes == eRemoveDupes) {
|
||||
// Check for repeat events. If a redundant event exists remove
|
||||
// original and put the new event at the end of the queue
|
||||
// so it is fired after the others
|
||||
|
@ -1506,6 +1535,16 @@ NS_IMETHODIMP nsDocAccessible::FlushPendingEvents()
|
|||
// so use that state now when firing the event
|
||||
nsAccEvent::PrepareForEvent(accessibleEvent);
|
||||
FireAccessibleEvent(accessibleEvent);
|
||||
// Post event processing
|
||||
if (eventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
|
||||
eventType == nsIAccessibleEvent::EVENT_DOM_DESTROY) {
|
||||
// Shutdown nsIAccessNode's or nsIAccessibles for any DOM nodes in this subtree
|
||||
nsCOMPtr<nsIDOMNode> hidingNode;
|
||||
accessibleEvent->GetDOMNode(getter_AddRefs(hidingNode));
|
||||
if (hidingNode) {
|
||||
RefreshNodes(hidingNode); // Will this bite us with asynch events
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1637,7 +1676,7 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
|
|||
nsCOMPtr<nsIAccessNode> childAccessNode;
|
||||
GetCachedAccessNode(childNode, getter_AddRefs(childAccessNode));
|
||||
nsCOMPtr<nsIAccessible> childAccessible = do_QueryInterface(childAccessNode);
|
||||
if (!childAccessible && isHiding) {
|
||||
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));
|
||||
|
@ -1668,14 +1707,16 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
|
|||
#endif
|
||||
|
||||
if (!isShowing) {
|
||||
// Fire EVENT_HIDE or EVENT_DOM_DESTROY if previous accessible existed for node being hidden.
|
||||
// Fire EVENT_ASYNCH_HIDE or EVENT_DOM_DESTROY if previous accessible existed for node being hidden.
|
||||
// Fire this before the accessible goes away.
|
||||
if (childAccessible) {
|
||||
PRUint32 removalEvent = isAsynch ? nsIAccessibleEvent::EVENT_ASYNCH_HIDE : nsIAccessibleEvent::EVENT_DOM_DESTROY;
|
||||
nsAccUtils::FireAccEvent(removalEvent, childAccessible, isAsynch);
|
||||
PRUint32 removalEventType = isAsynch ? nsIAccessibleEvent::EVENT_ASYNCH_HIDE :
|
||||
nsIAccessibleEvent::EVENT_DOM_DESTROY;
|
||||
nsCOMPtr<nsIAccessibleEvent> removalEvent =
|
||||
new nsAccEvent(removalEventType, childAccessible, nsnull, PR_TRUE);
|
||||
NS_ENSURE_TRUE(removalEvent, NS_ERROR_OUT_OF_MEMORY);
|
||||
FireDelayedAccessibleEvent(removalEvent, eCoalesceFromSameSubtree, 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
|
||||
|
@ -1704,18 +1745,15 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
|
|||
// nsIAccessibleStates::STATE_INVISIBLE for the event's accessible object.
|
||||
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);
|
||||
FireDelayedToolkitEvent(additionEvent, childNode, nsnull,
|
||||
eCoalesceFromSameSubtree, 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, PR_TRUE, isAsynch);
|
||||
childNode, nsnull, eAllowDupes, isAsynch);
|
||||
}
|
||||
|
||||
// Check to see if change occured inside an alert, and fire an EVENT_ALERT if it did
|
||||
|
@ -1724,7 +1762,8 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
|
|||
if (GetRoleAttribute(ancestor, role) &&
|
||||
StringEndsWith(role, NS_LITERAL_STRING(":alert"), nsCaseInsensitiveStringComparator())) {
|
||||
nsCOMPtr<nsIDOMNode> alertNode(do_QueryInterface(ancestor));
|
||||
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_ALERT, alertNode, nsnull, PR_FALSE, isAsynch);
|
||||
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_ALERT, alertNode, nsnull,
|
||||
eRemoveDupes, isAsynch);
|
||||
break;
|
||||
}
|
||||
ancestor = ancestor->GetParent();
|
||||
|
|
|
@ -102,20 +102,25 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap,
|
|||
// nsIAccessibleText
|
||||
NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
|
||||
|
||||
enum EDupeEventRule { eAllowDupes, eCoalesceFromSameSubtree, eRemoveDupes };
|
||||
|
||||
/**
|
||||
* 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 aAllowDupes - eAllowDupes: more than one event of the same type is allowed.
|
||||
* eCoalesceFromSameSubtree: if two events are in the same subtree,
|
||||
* only the event on ancestor is used
|
||||
* eRemoveDupes (default): 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, EDupeEventRule aAllowDupes = eRemoveDupes,
|
||||
PRBool aIsAsynch = PR_FALSE);
|
||||
|
||||
/**
|
||||
|
@ -129,7 +134,7 @@ class nsDocAccessible : public nsHyperTextAccessibleWrap,
|
|||
* an event asynchronous with the DOM
|
||||
*/
|
||||
nsresult FireDelayedAccessibleEvent(nsIAccessibleEvent *aEvent,
|
||||
PRBool aAllowDupes = PR_FALSE,
|
||||
EDupeEventRule aAllowDupes = eRemoveDupes,
|
||||
PRBool aIsAsynch = PR_FALSE);
|
||||
|
||||
void ShutdownChildDocuments(nsIDocShellTreeItem *aStart);
|
||||
|
|
|
@ -419,7 +419,7 @@ void nsRootAccessible::TryFireEarlyLoadEvent(nsIDOMNode *aDocNode)
|
|||
|
||||
// No frames or iframes, so we can fire the doc load finished event early
|
||||
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_INTERNAL_LOAD, aDocNode,
|
||||
nsnull, PR_FALSE);
|
||||
nsnull, eRemoveDupes);
|
||||
}
|
||||
|
||||
PRBool nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible,
|
||||
|
@ -511,7 +511,7 @@ PRBool nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible,
|
|||
}
|
||||
|
||||
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_FOCUS,
|
||||
finalFocusNode, nsnull, PR_FALSE, aIsAsynch);
|
||||
finalFocusNode, nsnull, eRemoveDupes, aIsAsynch);
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
|
|
@ -1372,7 +1372,8 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
|
|||
|
||||
#ifdef ACCESSIBILITY
|
||||
if (isAccessibilityActive &&
|
||||
aFrame->GetStyleVisibility()->IsVisible() != isVisible) {
|
||||
aFrame->GetStyleVisibility()->IsVisible() != isVisible &&
|
||||
!aFrame->GetPrevContinuation()) { // Primary frames only
|
||||
// XXX Visibility does not affect descendents with visibility set
|
||||
// Work on a separate, accurate mechanism for dealing with visibility changes.
|
||||
// A significant enough change occured that this part
|
||||
|
|
Загрузка…
Ссылка в новой задаче