From 2a91a9bd9d2232b03588f0932d8e0d7a1ff1346d Mon Sep 17 00:00:00 2001 From: "aaronleventhal%moonset.net" Date: Mon, 1 Oct 2007 18:27:13 +0000 Subject: [PATCH] Bug 396005. Severe performance degredation regression when a11y active. r=surkov, a=dsicore --- accessible/public/nsIAccessibleDocument.idl | 5 +- accessible/src/base/nsAccessible.cpp | 10 +++- accessible/src/base/nsDocAccessible.cpp | 61 ++++++++++++++++----- 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/accessible/public/nsIAccessibleDocument.idl b/accessible/public/nsIAccessibleDocument.idl index 7227d3af509..80ef42fdcf3 100644 --- a/accessible/public/nsIAccessibleDocument.idl +++ b/accessible/public/nsIAccessibleDocument.idl @@ -58,7 +58,7 @@ interface nsIDOMWindow; * * @status UNDER_REVIEW */ -[scriptable, uuid(d118c0e9-b5e7-4671-854a-65b4713d9552)] +[scriptable, uuid(81ddd75f-adbd-4a1c-b87c-6522bcea0596)] interface nsIAccessibleDocument : nsISupports { /** @@ -120,7 +120,8 @@ interface nsIAccessibleDocument : nsISupports * @return An first nsIAccessible found by crawling up the DOM node * to the document root. */ - nsIAccessible getAccessibleInParentChain(in nsIDOMNode aDOMNode); + nsIAccessible getAccessibleInParentChain(in nsIDOMNode aDOMNode, + in boolean aCanCreate); /** * A bit flag representing the type of ARIA properties which should be diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index cd0e38d5bfa..43f2b0acebf 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -152,6 +152,12 @@ PRBool nsAccessible::IsTextInterfaceSupportCorrect(nsIAccessible *aAccessible) { PRBool foundText = PR_FALSE; + nsCOMPtr accDoc = do_QueryInterface(aAccessible); + if (accDoc) { + // Don't test for accessible docs, it makes us create accessibles too + // early and fire mutation events before we need to + return PR_TRUE; + } nsCOMPtr child, nextSibling; aAccessible->GetFirstChild(getter_AddRefs(child)); while (child) { @@ -584,7 +590,7 @@ NS_IMETHODIMP nsAccessible::GetParent(nsIAccessible ** aParent) nsCOMPtr docAccessible(GetDocAccessible()); NS_ENSURE_TRUE(docAccessible, NS_ERROR_FAILURE); - return docAccessible->GetAccessibleInParentChain(mDOMNode, aParent); + return docAccessible->GetAccessibleInParentChain(mDOMNode, PR_TRUE, aParent); } NS_IMETHODIMP nsAccessible::GetCachedParent(nsIAccessible ** aParent) @@ -1162,7 +1168,7 @@ nsAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY, if (!accessible) { // No accessible for the node with the point, so find the first // accessible in the DOM parent chain - accDocument->GetAccessibleInParentChain(relevantNode, + accDocument->GetAccessibleInParentChain(relevantNode, PR_TRUE, getter_AddRefs(accessible)); if (!accessible) { NS_IF_ADDREF(*aAccessible = fallbackAnswer); diff --git a/accessible/src/base/nsDocAccessible.cpp b/accessible/src/base/nsDocAccessible.cpp index 56a4971aab0..cf3b34a1f3e 100644 --- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -1301,7 +1301,7 @@ nsDocAccessible::FireTextChangeEventForText(nsIContent *aContent, return; nsCOMPtr accessible; - nsresult rv = GetAccessibleInParentChain(node, getter_AddRefs(accessible)); + nsresult rv = GetAccessibleInParentChain(node, PR_TRUE, getter_AddRefs(accessible)); if (NS_FAILED(rv) || !accessible) return; @@ -1567,7 +1567,7 @@ NS_IMETHODIMP nsDocAccessible::FlushPendingEvents() accessibleEvent->GetDOMNode(getter_AddRefs(domNode)); if (domNode && domNode != mDOMNode) { if (!containerAccessible) - GetAccessibleInParentChain(domNode, + GetAccessibleInParentChain(domNode, PR_TRUE, getter_AddRefs(containerAccessible)); nsCOMPtr textChangeEvent = @@ -1757,15 +1757,39 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild, if (!IsNodeRelevant(childNode)) { return NS_OK; // Don't fire event unless it can be for an attached accessible } - if (!mIsContentLoaded && mAccessNodeCache.Count() <= 1) { - // Still loading and no accessibles has yet been created other than this - // doc accessible. In this case we optimize - // by not firing SHOW/HIDE/REORDER events for every document mutation - // caused by page load, since AT is not going to want to grab the - // document and listen to these changes until after the page is first loaded - // Leave early, and ensure mAccChildCount stays uninitialized instead of 0, - // which it is if anyone asks for its children right now. - return InvalidateChildren(); + if (!mIsContentLoaded) { + // Still loading document + if (mAccessNodeCache.Count() <= 1) { + // Still loading and no accessibles has yet been created other than this + // doc accessible. In this case we optimize + // by not firing SHOW/HIDE/REORDER events for every document mutation + // caused by page load, since AT is not going to want to grab the + // document and listen to these changes until after the page is first loaded + // Leave early, and ensure mAccChildCount stays uninitialized instead of 0, + // which it is if anyone asks for its children right now. + return InvalidateChildren(); + } + if (aChangeEventType == nsIAccessibleEvent::EVENT_DOM_CREATE) { + nsCOMPtr presShell = GetPresShell(); + NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); + nsIEventStateManager *esm = presShell->GetPresContext()->EventStateManager(); + NS_ENSURE_TRUE(esm, NS_ERROR_FAILURE); + if (!esm->IsHandlingUserInputExternal()) { + // Adding content during page load, but not caused by user input + // Just invalidate accessible hierarchy and return, + // otherwise the page load time slows down way too much + nsCOMPtr containerAccessible; + GetAccessibleInParentChain(childNode, PR_FALSE, getter_AddRefs(containerAccessible)); + if (!containerAccessible) { + containerAccessible = this; + } + nsCOMPtr privateContainer = do_QueryInterface(containerAccessible); + return privateContainer->InvalidateChildren(); + } + // else: user input, so we must fall through and for full handling, + // e.g. fire the mutation events. Note: user input could cause DOM_CREATE + // during page load if user typed into an input field or contentEditable area + } } // Update last change state information @@ -1803,7 +1827,7 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild, #endif nsCOMPtr containerAccessible; - GetAccessibleInParentChain(childNode, getter_AddRefs(containerAccessible)); + GetAccessibleInParentChain(childNode, PR_TRUE, getter_AddRefs(containerAccessible)); if (!containerAccessible) { containerAccessible = this; } @@ -1888,6 +1912,7 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild, NS_IMETHODIMP nsDocAccessible::GetAccessibleInParentChain(nsIDOMNode *aNode, + PRBool aCanCreate, nsIAccessible **aAccessible) { // Find accessible in parent chain of DOM nodes, or return null @@ -1911,8 +1936,16 @@ nsDocAccessible::GetAccessibleInParentChain(nsIDOMNode *aNode, if (NS_SUCCEEDED(accService->GetRelevantContentNodeFor(currentNode, getter_AddRefs(relevantNode))) && relevantNode) { currentNode = relevantNode; } - - accService->GetAccessibleInWeakShell(currentNode, mWeakShell, aAccessible); + if (aCanCreate) { + accService->GetAccessibleInWeakShell(currentNode, mWeakShell, aAccessible); + } + else { // Only return cached accessibles, don't create anything + nsCOMPtr accessNode; + GetCachedAccessNode(currentNode, getter_AddRefs(accessNode)); // AddRefs + if (accessNode) { + CallQueryInterface(accessNode, aAccessible); // AddRefs + } + } } while (!*aAccessible); return NS_OK;