From f80f334157c6b8ac5f2915c55a4bf2d299f9a356 Mon Sep 17 00:00:00 2001 From: "aaronleventhal@moonset.net" Date: Thu, 15 Nov 2007 17:38:33 -0800 Subject: [PATCH] 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 --- accessible/src/base/nsAccessNode.cpp | 17 ++++ accessible/src/base/nsDocAccessible.cpp | 112 ++++++++++++------------ layout/forms/nsFileControlFrame.cpp | 9 +- 3 files changed, 82 insertions(+), 56 deletions(-) diff --git a/accessible/src/base/nsAccessNode.cpp b/accessible/src/base/nsAccessNode.cpp index a9361e317bcf..6dcb0aa1ccdd 100755 --- a/accessible/src/base/nsAccessNode.cpp +++ b/accessible/src/base/nsAccessNode.cpp @@ -175,12 +175,29 @@ NS_IMETHODIMP nsAccessNode::Init() return NS_ERROR_FAILURE; } } + void* uniqueID; GetUniqueID(&uniqueID); nsCOMPtr 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 content = do_QueryInterface(mDOMNode); + if (content && (content->IsNativeAnonymous() || + content->GetBindingParent())) { + // Specific examples of where this is used: and + nsCOMPtr parentAccessible; + docAccessible->GetAccessibleInParentChain(mDOMNode, PR_TRUE, getter_AddRefs(parentAccessible)); + if (parentAccessible) { + PRInt32 childCountUnused; + parentAccessible->GetChildCount(&childCountUnused); + } + } + #ifdef DEBUG_A11Y mIsInitialized = PR_TRUE; #endif diff --git a/accessible/src/base/nsDocAccessible.cpp b/accessible/src/base/nsDocAccessible.cpp index 6f4bf7e3de6a..7567ed5b172b 100644 --- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -1666,71 +1666,75 @@ void nsDocAccessible::FlushEventsCallback(nsITimer *aTimer, void *aClosure) void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode) { - nsCOMPtr iterNode(aStartNode), nextNode; nsCOMPtr accessNode; + GetCachedAccessNode(aStartNode, getter_AddRefs(accessNode)); + nsCOMPtr 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 accessible(do_QueryInterface(accessNode)); + if (accessible) { + nsCOMPtr privateAccessible = do_QueryInterface(accessible); + NS_ASSERTION(privateAccessible, "No nsPIAccessible for nsIAccessible"); + nsCOMPtr childAccessible; + privateAccessible->GetCachedFirstChild(getter_AddRefs(childAccessible)); + if (childAccessible) { // Has cached children that need to be shut down + nsCOMPtr 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(this)) { - - nsCOMPtr 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 domNode; - accessNode->GetDOMNode(getter_AddRefs(domNode)); - nsCOMPtr 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 childAccessNode; + children->QueryElementAt(index, NS_GET_IID(nsIAccessNode), + getter_AddRefs(childAccessNode)); + childAccessNode->GetDOMNode(getter_AddRefs(iterNode)); + nsCOMPtr 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 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(this)) { + // Fire menupopup end if a menu goes away + PRUint32 role = Role(accessible); + if (role == nsIAccessibleRole::ROLE_MENUPOPUP) { + nsCOMPtr domNode; + accessNode->GetDOMNode(getter_AddRefs(domNode)); + nsCOMPtr 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 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, diff --git a/layout/forms/nsFileControlFrame.cpp b/layout/forms/nsFileControlFrame.cpp index f54926eba91b..545db00e39a1 100644 --- a/layout/forms/nsFileControlFrame.cpp +++ b/layout/forms/nsFileControlFrame.cpp @@ -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 accService = do_GetService("@mozilla.org/accessibilityService;1"); + + if (accService) { + return accService->CreateHTMLGenericAccessible(static_cast(this), aAccessible); + } + return NS_ERROR_FAILURE; } #endif