Bug 385070. Accessible subtrees for anonymous content need to be shut down. Patch by Evan Yan. r=aaronlev, r+sr=roc for layout part, a=dsicor
git-svn-id: svn://10.0.0.236/trunk@239742 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -175,12 +175,29 @@ NS_IMETHODIMP nsAccessNode::Init()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
void* uniqueID;
|
||||
GetUniqueID(&uniqueID);
|
||||
nsCOMPtr<nsPIAccessibleDocument> privateDocAccessible =
|
||||
do_QueryInterface(docAccessible);
|
||||
NS_ASSERTION(privateDocAccessible, "No private docaccessible for docaccessible");
|
||||
privateDocAccessible->CacheAccessNode(uniqueID, this);
|
||||
|
||||
// Make sure an ancestor in real content is cached
|
||||
// so that nsDocAccessible::RefreshNodes() can find the anonymous subtree to release when
|
||||
// the root node goes away
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(mDOMNode);
|
||||
if (content && (content->IsNativeAnonymous() ||
|
||||
content->GetBindingParent())) {
|
||||
// Specific examples of where this is used: <input type="file"> and <xul:findbar>
|
||||
nsCOMPtr<nsIAccessible> parentAccessible;
|
||||
docAccessible->GetAccessibleInParentChain(mDOMNode, PR_TRUE, getter_AddRefs(parentAccessible));
|
||||
if (parentAccessible) {
|
||||
PRInt32 childCountUnused;
|
||||
parentAccessible->GetChildCount(&childCountUnused);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_A11Y
|
||||
mIsInitialized = PR_TRUE;
|
||||
#endif
|
||||
|
||||
@@ -1635,14 +1635,6 @@ NS_IMETHODIMP nsDocAccessible::FlushPendingEvents()
|
||||
// Post event processing
|
||||
if (eventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
|
||||
eventType == nsIAccessibleEvent::EVENT_DOM_DESTROY) {
|
||||
// Invalidate children
|
||||
nsCOMPtr<nsIAccessible> containerAccessible;
|
||||
accessible->GetParent(getter_AddRefs(containerAccessible));
|
||||
nsCOMPtr<nsPIAccessible> privateContainerAccessible =
|
||||
do_QueryInterface(containerAccessible);
|
||||
if (privateContainerAccessible) {
|
||||
privateContainerAccessible->InvalidateChildren();
|
||||
}
|
||||
// Shutdown nsIAccessNode's or nsIAccessibles for any DOM nodes in this subtree
|
||||
nsCOMPtr<nsIDOMNode> hidingNode;
|
||||
accessibleEvent->GetDOMNode(getter_AddRefs(hidingNode));
|
||||
@@ -1666,71 +1658,78 @@ void nsDocAccessible::FlushEventsCallback(nsITimer *aTimer, void *aClosure)
|
||||
|
||||
void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> iterNode(aStartNode), nextNode;
|
||||
nsCOMPtr<nsIAccessNode> accessNode;
|
||||
GetCachedAccessNode(aStartNode, getter_AddRefs(accessNode));
|
||||
nsCOMPtr<nsIDOMNode> nextNode, iterNode;
|
||||
|
||||
do {
|
||||
GetCachedAccessNode(iterNode, getter_AddRefs(accessNode));
|
||||
if (accessNode) {
|
||||
// Accessibles that implement their own subtrees,
|
||||
// like html combo boxes and xul trees must shutdown all of their own
|
||||
// children when they override Shutdown()
|
||||
// Shut down accessible subtree, which may have been created for
|
||||
// anonymous content subtree
|
||||
nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
|
||||
if (accessible) {
|
||||
nsCOMPtr<nsPIAccessible> privateAccessible = do_QueryInterface(accessible);
|
||||
NS_ASSERTION(privateAccessible, "No nsPIAccessible for nsIAccessible");
|
||||
|
||||
// Don't shutdown our doc object!
|
||||
if (accessNode != static_cast<nsIAccessNode*>(this)) {
|
||||
nsCOMPtr<nsIAccessible> childAccessible;
|
||||
// we only need to shutdown the accessibles here if one of them has been created
|
||||
privateAccessible->GetCachedFirstChild(getter_AddRefs(childAccessible));
|
||||
if (childAccessible) {
|
||||
nsCOMPtr<nsIArray> children;
|
||||
// use GetChildren() to fetch children at one time, instead of using
|
||||
// GetNextSibling(), because after we shutdown the first child,
|
||||
// mNextSibling will be set null.
|
||||
accessible->GetChildren(getter_AddRefs(children));
|
||||
|
||||
nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
|
||||
if (accessible) {
|
||||
// Fire menupopupend events for menu popups that go away
|
||||
PRUint32 role = Role(accessible);
|
||||
if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
||||
nsCOMPtr<nsIDOMNode> domNode;
|
||||
accessNode->GetDOMNode(getter_AddRefs(domNode));
|
||||
nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(domNode));
|
||||
if (!popup) {
|
||||
// Popup elements already fire these via DOMMenuInactive
|
||||
// handling in nsRootAccessible::HandleEvent
|
||||
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
|
||||
accessible);
|
||||
}
|
||||
}
|
||||
PRUint32 childCount;
|
||||
children->GetLength(&childCount);
|
||||
for (PRUint32 index = 0; index < childCount; index++) {
|
||||
nsCOMPtr<nsIAccessNode> childAccessNode;
|
||||
children->QueryElementAt(index, NS_GET_IID(nsIAccessNode),
|
||||
getter_AddRefs(childAccessNode));
|
||||
childAccessNode->GetDOMNode(getter_AddRefs(iterNode));
|
||||
nsCOMPtr<nsIContent> iterContent = do_QueryInterface(iterNode);
|
||||
if (iterContent && (iterContent->IsNativeAnonymous() ||
|
||||
iterContent->GetBindingParent())) {
|
||||
// GetBindingParent() check is a perf win -- make sure we don't
|
||||
// shut down the same subtree twice since we'll reach non-anon content via
|
||||
// DOM traversal later in this method
|
||||
RefreshNodes(iterNode);
|
||||
}
|
||||
|
||||
void *uniqueID;
|
||||
accessNode->GetUniqueID(&uniqueID);
|
||||
nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(accessNode));
|
||||
privateAccessNode->Shutdown();
|
||||
// Remove from hash table as well
|
||||
mAccessNodeCache.Remove(uniqueID);
|
||||
}
|
||||
}
|
||||
|
||||
iterNode->GetFirstChild(getter_AddRefs(nextNode));
|
||||
if (nextNode) {
|
||||
iterNode = nextNode;
|
||||
continue;
|
||||
// Shutdown ordinary content subtree as well -- there may be
|
||||
// access node children which are not full accessible objects
|
||||
aStartNode->GetFirstChild(getter_AddRefs(nextNode));
|
||||
while (nextNode) {
|
||||
nextNode.swap(iterNode);
|
||||
RefreshNodes(iterNode);
|
||||
iterNode->GetNextSibling(getter_AddRefs(nextNode));
|
||||
}
|
||||
|
||||
if (iterNode == aStartNode)
|
||||
break;
|
||||
iterNode->GetNextSibling(getter_AddRefs(nextNode));
|
||||
if (nextNode) {
|
||||
iterNode = nextNode;
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
iterNode->GetParentNode(getter_AddRefs(nextNode));
|
||||
if (!nextNode || nextNode == aStartNode) {
|
||||
return;
|
||||
// Don't shutdown our doc object!
|
||||
if (accessNode && accessNode != static_cast<nsIAccessNode*>(this)) {
|
||||
// Fire menupopup end if a menu goes away
|
||||
PRUint32 role = Role(accessible);
|
||||
if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
||||
nsCOMPtr<nsIDOMNode> domNode;
|
||||
accessNode->GetDOMNode(getter_AddRefs(domNode));
|
||||
nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(domNode));
|
||||
if (!popup) {
|
||||
// Popup elements already fire these via DOMMenuInactive
|
||||
// handling in nsRootAccessible::HandleEvent
|
||||
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
|
||||
accessible);
|
||||
}
|
||||
}
|
||||
nextNode->GetNextSibling(getter_AddRefs(iterNode));
|
||||
if (iterNode)
|
||||
break;
|
||||
iterNode = nextNode;
|
||||
} while (PR_TRUE);
|
||||
// Shut down the actual accessible or access node
|
||||
void *uniqueID;
|
||||
accessNode->GetUniqueID(&uniqueID);
|
||||
nsCOMPtr<nsPIAccessNode> privateAccessNode(do_QueryInterface(accessNode));
|
||||
privateAccessNode->Shutdown();
|
||||
// Remove from hash table as well
|
||||
mAccessNodeCache.Remove(uniqueID);
|
||||
}
|
||||
}
|
||||
while (iterNode && iterNode != aStartNode);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
|
||||
|
||||
@@ -603,8 +603,13 @@ nsFileControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
#ifdef ACCESSIBILITY
|
||||
NS_IMETHODIMP nsFileControlFrame::GetAccessible(nsIAccessible** aAccessible)
|
||||
{
|
||||
// No accessible object for file control, only for child text frame and button
|
||||
*aAccessible = nsnull;
|
||||
// Accessible object exists just to hold onto its children, for later shutdown
|
||||
nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
|
||||
|
||||
if (accService) {
|
||||
return accService->CreateHTMLGenericAccessible(static_cast<nsIFrame*>(this), aAccessible);
|
||||
}
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user