Bug 385070. Nodes in anonymous content trees that are hidden not being shut down. Patch by Evan Yan. r=aaronlev, r+sr=roc, a=mconnor

This commit is contained in:
aaronleventhal@moonset.net 2007-11-15 17:38:33 -08:00
parent 9f49624e96
commit 6c34d177ea
3 changed files with 82 additions and 56 deletions

View File

@ -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

View File

@ -1666,71 +1666,75 @@ 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");
nsCOMPtr<nsIAccessible> childAccessible;
privateAccessible->GetCachedFirstChild(getter_AddRefs(childAccessible));
if (childAccessible) { // Has cached children that need to be shut down
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));
// Don't shutdown our doc object!
if (accessNode != static_cast<nsIAccessNode*>(this)) {
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;
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,

View File

@ -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