diff --git a/mozilla/accessible/src/base/nsAccessNode.h b/mozilla/accessible/src/base/nsAccessNode.h index 9f7efccf0a7..33ab98823f5 100755 --- a/mozilla/accessible/src/base/nsAccessNode.h +++ b/mozilla/accessible/src/base/nsAccessNode.h @@ -59,6 +59,7 @@ class nsIFrame; class nsIDOMNodeList; enum { eChildCountUninitialized = 0xffff }; +enum { eSiblingsUninitialized = -1, eSiblingsWalkNormalDOM = -2 }; #define ACCESSIBLE_BUNDLE_URL "chrome://global-platform/locale/accessible.properties" #define PLATFORM_KEYS_BUNDLE_URL "chrome://global-platform/locale/platformKeys.properties" diff --git a/mozilla/accessible/src/base/nsAccessible.cpp b/mozilla/accessible/src/base/nsAccessible.cpp index 3b00506517a..fff8ab803d4 100644 --- a/mozilla/accessible/src/base/nsAccessible.cpp +++ b/mozilla/accessible/src/base/nsAccessible.cpp @@ -278,15 +278,6 @@ NS_IMETHODIMP nsAccessible::GetNextSibling(nsIAccessible * *aNextSibling) // This node has been shut down return NS_ERROR_FAILURE; } - if (!mParent) { - nsCOMPtr parent; - GetParent(getter_AddRefs(parent)); - if (parent) { - PRInt32 numChildren; - parent->GetChildCount(&numChildren); // Make sure we cache all of the children - } - } - if (mNextSibling || !mParent) { // If no parent, don't try to calculate a new sibling // It either means we're at the root or shutting down the parent @@ -296,7 +287,22 @@ NS_IMETHODIMP nsAccessible::GetNextSibling(nsIAccessible * *aNextSibling) return NS_OK; } - return NS_ERROR_FAILURE; + // Last argument of PR_TRUE indicates to walk anonymous content + nsAccessibleTreeWalker walker(mWeakShell, mDOMNode, PR_TRUE); + + if (NS_SUCCEEDED(walker.GetNextSibling())) { + *aNextSibling = walker.mState.accessible; + NS_ADDREF(*aNextSibling); + nsCOMPtr privateAcc(do_QueryInterface(*aNextSibling)); + privateAcc->SetParent(mParent); + + mNextSibling = *aNextSibling; + } + + if (!mNextSibling) + mNextSibling = DEAD_END_ACCESSIBLE; + + return NS_OK; } /* readonly attribute nsIAccessible previousSibling; */ @@ -309,26 +315,17 @@ NS_IMETHODIMP nsAccessible::GetPreviousSibling(nsIAccessible * *aPreviousSibling return NS_ERROR_FAILURE; } - if (!mParent) { - nsCOMPtr parent; - if (NS_FAILED(GetParent(getter_AddRefs(parent)))) { - return NS_ERROR_FAILURE; - } + // Last argument of PR_TRUE indicates to walk anonymous content + nsAccessibleTreeWalker walker(mWeakShell, mDOMNode, PR_TRUE); + if (NS_SUCCEEDED(walker.GetPreviousSibling())) { + *aPreviousSibling = walker.mState.accessible; + NS_ADDREF(*aPreviousSibling); + // Use last walker state to cache data on prev accessible + nsCOMPtr privateAcc(do_QueryInterface(*aPreviousSibling)); + privateAcc->SetParent(mParent); } - nsCOMPtr testAccessible, prevSibling; - mParent->GetFirstChild(getter_AddRefs(testAccessible)); - while (testAccessible) { - prevSibling = testAccessible; - prevSibling->GetNextSibling(getter_AddRefs(testAccessible)); - } - - if (!prevSibling) { - return NS_ERROR_FAILURE; - } - - NS_ADDREF(*aPreviousSibling = prevSibling); - return NS_OK; + return NS_OK; } /* readonly attribute nsIAccessible firstChild; */ @@ -394,7 +391,7 @@ void nsAccessible::CacheChildren(PRBool aWalkAnonContent) // Seed the frame hint early while we're still on a container node. // This is better than doing the GetPrimaryFrameFor() later on // a text node, because text nodes aren't in the frame map. - walker.mState.frame = GetFrame(); + walker.mState.frameHint = GetFrame(); nsCOMPtr privatePrevAccessible; mAccChildCount = 0; diff --git a/mozilla/accessible/src/base/nsAccessibleTreeWalker.cpp b/mozilla/accessible/src/base/nsAccessibleTreeWalker.cpp index 2138df71d35..afffaf87b27 100755 --- a/mozilla/accessible/src/base/nsAccessibleTreeWalker.cpp +++ b/mozilla/accessible/src/base/nsAccessibleTreeWalker.cpp @@ -37,14 +37,13 @@ * ***** END LICENSE BLOCK ***** */ #include "nsAccessibleTreeWalker.h" -#include "nsAccessibilityAtoms.h" +#include "nsWeakReference.h" #include "nsAccessNode.h" #include "nsIServiceManager.h" #include "nsIContent.h" #include "nsIDOMXULElement.h" #include "nsIPresShell.h" #include "nsIFrame.h" -#include "nsWeakReference.h" nsAccessibleTreeWalker::nsAccessibleTreeWalker(nsIWeakReference* aPresShell, nsIDOMNode* aNode, PRBool aWalkAnonContent): mWeakShell(aPresShell), @@ -55,7 +54,7 @@ nsAccessibleTreeWalker::nsAccessibleTreeWalker(nsIWeakReference* aPresShell, nsI mState.siblingIndex = eSiblingsUninitialized; mState.siblingList = nsnull; mState.isHidden = false; - mState.frame = nsnull; + mState.frameHint = nsnull; if (aWalkAnonContent) { nsCOMPtr presShell(do_QueryReferent(mWeakShell)); @@ -66,6 +65,7 @@ nsAccessibleTreeWalker::nsAccessibleTreeWalker(nsIWeakReference* aPresShell, nsI } } MOZ_COUNT_CTOR(nsAccessibleTreeWalker); + mInitialState = mState; // deep copy } nsAccessibleTreeWalker::~nsAccessibleTreeWalker() @@ -109,34 +109,33 @@ NS_IMETHODIMP nsAccessibleTreeWalker::GetFullTreeParentNode(nsIDOMNode *aChildNo void nsAccessibleTreeWalker::GetKids(nsIDOMNode *aParentNode) { - nsCOMPtr parentContent(do_QueryInterface(aParentNode)); - if (!parentContent || !parentContent->IsContentOfType(nsIContent::eHTML)) { - mState.frame = nsnull; // Don't walk frames in non-HTML content, just walk the DOM. - } + nsCOMPtr content(do_QueryInterface(aParentNode)); - PushState(); - UpdateFrame(PR_TRUE); - - // Walk frames? UpdateFrame() sets this when it sees anonymous frames - if (mState.siblingIndex == eSiblingsWalkFrames) { - return; - } - - // Walk anonymous content? Not currently used for HTML -- anonymous content there uses frame walking - if (parentContent && !parentContent->IsContentOfType(nsIContent::eHTML) && mBindingManager) { - // Walk anonymous content - mBindingManager->GetXBLChildNodesFor(parentContent, getter_AddRefs(mState.siblingList)); // returns null if no anon nodes - if (mState.siblingList) { - mState.siblingIndex = 0; // Indicates our index into the sibling list - mState.siblingList->Item(0, getter_AddRefs(mState.domNode)); - return; - } - } - - // Walk normal DOM mState.siblingIndex = eSiblingsWalkNormalDOM; // Default value - indicates no sibling list - if (aParentNode) { - aParentNode->GetFirstChild(getter_AddRefs(mState.domNode)); + + if (content && mBindingManager) { + mBindingManager->GetXBLChildNodesFor(content, getter_AddRefs(mState.siblingList)); // returns null if no anon nodes + if (mState.siblingList) + mState.siblingIndex = 0; // Indicates our index into the sibling list + } +} + +void nsAccessibleTreeWalker::GetSiblings(nsIDOMNode *aOneOfTheSiblings) +{ + nsCOMPtr node; + + mState.siblingIndex = eSiblingsWalkNormalDOM; // Default value + + if (NS_SUCCEEDED(GetFullTreeParentNode(aOneOfTheSiblings, getter_AddRefs(node)))) { + GetKids(node); + if (mState.siblingList) { // Init index by seeing how far we are into list + if (mState.domNode == mInitialState.domNode) + mInitialState = mState; // deep copy, we'll use sibling info for caching + while (NS_SUCCEEDED(mState.siblingList->Item(mState.siblingIndex, getter_AddRefs(node))) && node != mState.domNode) { + NS_ASSERTION(node, "Something is terribly wrong - the child is not in it's parent's children!"); + ++mState.siblingIndex; + } + } } } @@ -158,19 +157,19 @@ NS_IMETHODIMP nsAccessibleTreeWalker::GetParent() NS_IMETHODIMP nsAccessibleTreeWalker::PopState() { - nsIFrame *frameParent = mState.frame? mState.frame->GetParent(): nsnull; + nsIFrame *frameHintParent = mState.frameHint? mState.frameHint->GetParent(): nsnull; if (mState.prevState) { WalkState *toBeDeleted = mState.prevState; mState = *mState.prevState; // deep copy mState.isHidden = PR_FALSE; // If we were in a child, the parent wasn't hidden - if (!mState.frame) { - mState.frame = frameParent; + if (!mState.frameHint) { + mState.frameHint = frameHintParent; } delete toBeDeleted; return NS_OK; } ClearState(); - mState.frame = frameParent; + mState.frameHint = frameHintParent; mState.isHidden = PR_FALSE; return NS_ERROR_FAILURE; } @@ -195,42 +194,54 @@ NS_IMETHODIMP nsAccessibleTreeWalker::PushState() return NS_OK; } -void nsAccessibleTreeWalker::GetNextDOMNode() -{ - // Get next DOM node - if (mState.siblingIndex == eSiblingsWalkNormalDOM) { - mState.domNode->GetNextSibling(getter_AddRefs(mState.domNode)); - } - else if (mState.siblingIndex == eSiblingsWalkFrames) { - mState.domNode = mState.frame? do_QueryInterface(mState.frame->GetContent()): nsnull; - } - else { - mState.siblingList->Item(++mState.siblingIndex, getter_AddRefs(mState.domNode)); - } -} - NS_IMETHODIMP nsAccessibleTreeWalker::GetNextSibling() { - // Make sure mState.prevState and mState.siblingIndex are initialized so we can walk forward - NS_ASSERTION(mState.prevState && mState.siblingIndex != eSiblingsUninitialized, - "Error - GetNextSibling() only works after a GetFirstChild(), so we must have a prevState."); mState.accessible = nsnull; - while (PR_TRUE) { - // Get next frame - UpdateFrame(PR_FALSE); - GetNextDOMNode(); + // Make sure mState.siblingIndex and mState.siblingList are initialized + if (mState.siblingIndex == eSiblingsUninitialized) + GetSiblings(mState.domNode); - if (!mState.domNode) { // Done with current siblings - PopState(); // Use parent - go up in stack. Can always pop state because we have to start with a GetFirstChild(). - if (!mState.prevState) { + // get next sibling + nsCOMPtr next; + + while (PR_TRUE) { + if (mState.siblingIndex == eSiblingsWalkNormalDOM) + mState.domNode->GetNextSibling(getter_AddRefs(next)); + else + mState.siblingList->Item(++mState.siblingIndex, getter_AddRefs(next)); + + if (!next) { // Done with siblings + // if no DOM parent or DOM parent is accessible fail + nsCOMPtr parent; + if (NS_FAILED(GetFullTreeParentNode(mState.domNode, getter_AddRefs(parent)))) + break; // Failed - can't get parent node, we're at the top + + if (NS_FAILED(PopState())) { // Use parent - go up in stack + mState.domNode = parent; + } + if (mState.siblingIndex == eSiblingsUninitialized) + GetSiblings(mState.domNode); + + if (GetAccessible()) { mState.accessible = nsnull; - break; // Back to original accessible that we did GetFirstChild() from + break; // Failed - anything after this in the tree is in a new group of siblings } } - else if ((mState.domNode != mState.prevState->domNode && GetAccessible()) || - NS_SUCCEEDED(GetFirstChild())) { - return NS_OK; // if next is accessible, use it + else { + UpdateFrameHint(next, false); + // if next is accessible, use it + mState.domNode = next; + if (GetAccessible()) + return NS_OK; + + // otherwise call first on next + mState.domNode = next; + if (NS_SUCCEEDED(GetFirstChild())) + return NS_OK; + + // If no results, keep recursiom going - call next on next + mState.domNode = next; } } return NS_ERROR_FAILURE; @@ -238,38 +249,87 @@ NS_IMETHODIMP nsAccessibleTreeWalker::GetNextSibling() NS_IMETHODIMP nsAccessibleTreeWalker::GetFirstChild() { - mState.accessible = nsnull; - if (mState.isHidden || !mState.domNode) { + if (mState.isHidden) { return NS_ERROR_FAILURE; } + mState.accessible = nsnull; - nsCOMPtr parent(mState.domNode); - GetKids(parent); // Side effects change our state (mState) + if (!mState.domNode) + return NS_ERROR_FAILURE; + + nsCOMPtr next, parent(mState.domNode); + + PushState(); // Save old state + + GetKids(parent); // Side effects change our state + + if (mState.siblingIndex == eSiblingsWalkNormalDOM) // Indicates we must use normal DOM calls to traverse here + parent->GetFirstChild(getter_AddRefs(next)); + else // Use the sibling list - there are anonymous content nodes in here + mState.siblingList->Item(0, getter_AddRefs(next)); + UpdateFrameHint(next, true); // Recursive loop: depth first search for first accessible child - while (mState.domNode) { - if ((mState.domNode != parent && GetAccessible()) || NS_SUCCEEDED(GetFirstChild())) + while (next) { + mState.domNode = next; + if (GetAccessible() || NS_SUCCEEDED(GetFirstChild())) return NS_OK; - UpdateFrame(PR_FALSE); - GetNextDOMNode(); + if (mState.siblingIndex == eSiblingsWalkNormalDOM) // Indicates we must use normal DOM calls to traverse here + mState.domNode->GetNextSibling(getter_AddRefs(next)); + else + mState.siblingList->Item(++mState.siblingIndex, getter_AddRefs(next)); + UpdateFrameHint(next, false); } PopState(); // Return to previous state return NS_ERROR_FAILURE; } -void nsAccessibleTreeWalker::UpdateFrame(PRBool aTryFirstChild) +NS_IMETHODIMP nsAccessibleTreeWalker::GetChildBefore(nsIDOMNode* aParent, nsIDOMNode* aChild) { - if (mState.frame) { - mState.frame = aTryFirstChild? mState.frame->GetFirstChild(nsnull) : - mState.frame->GetNextSibling(); -#ifndef MOZ_ACCESSIBILITY_ATK - if (mState.frame && mState.siblingIndex <0 && - mState.frame->IsGeneratedContentFrame()) { - mState.domNode = do_QueryInterface(mState.frame->GetContent()); - mState.siblingIndex = eSiblingsWalkFrames; + mState.accessible = nsnull; + mState.domNode = aParent; + + if (!mState.domNode || NS_FAILED(GetFirstChild()) || mState.domNode == aChild) + return NS_ERROR_FAILURE; // if the first child is us, then we fail, because there is no child before the first + + nsCOMPtr prevDOMNode(mState.domNode); + nsCOMPtr prevAccessible(mState.accessible); + + while (mState.domNode && NS_SUCCEEDED(GetNextSibling()) && mState.domNode != aChild) { + prevDOMNode = mState.domNode; + prevAccessible = mState.accessible; + } + + mState.accessible = prevAccessible; + mState.domNode = prevDOMNode; + + return NS_OK; +} + +NS_IMETHODIMP nsAccessibleTreeWalker::GetPreviousSibling() +{ + nsCOMPtr child(mState.domNode); + nsresult rv = GetParent(); + if (NS_SUCCEEDED(rv)) + rv = GetChildBefore(mState.domNode, child); + return rv; +} + +NS_IMETHODIMP nsAccessibleTreeWalker::GetLastChild() +{ + return GetChildBefore(mState.domNode, nsnull); +} + +void nsAccessibleTreeWalker::UpdateFrameHint(nsIDOMNode *aStartNode, PRBool aTryFirstChild) +{ + if (mState.frameHint) { + nsIFrame *testFrame = aTryFirstChild? mState.frameHint->GetFirstChild(nsnull) : + mState.frameHint->GetNextSibling(); + nsCOMPtr content(do_QueryInterface(aStartNode)); + if (testFrame && content && content == testFrame->GetContent()) { + mState.frameHint = testFrame; } -#endif } } @@ -286,7 +346,7 @@ PRBool nsAccessibleTreeWalker::GetAccessible() nsCOMPtr presShell(do_QueryReferent(mWeakShell)); if (NS_SUCCEEDED(mAccService->GetAccessible(mState.domNode, presShell, mWeakShell, - &mState.frame, &mState.isHidden, + &mState.frameHint, &mState.isHidden, getter_AddRefs(mState.accessible)))) { NS_ASSERTION(mState.accessible, "No accessible but no failure return code"); return true; diff --git a/mozilla/accessible/src/base/nsAccessibleTreeWalker.h b/mozilla/accessible/src/base/nsAccessibleTreeWalker.h index 497c9bed100..e96c0481b60 100755 --- a/mozilla/accessible/src/base/nsAccessibleTreeWalker.h +++ b/mozilla/accessible/src/base/nsAccessibleTreeWalker.h @@ -50,14 +50,12 @@ #include "nsIBindingManager.h" #include "nsIWeakReference.h" -enum { eSiblingsUninitialized = -1, eSiblingsWalkNormalDOM = -2, eSiblingsWalkFrames = -3 }; - struct WalkState { nsCOMPtr accessible; nsCOMPtr domNode; nsCOMPtr siblingList; WalkState *prevState; - nsIFrame *frame; // Helps avoid GetPrimaryFrameFor() calls + nsIFrame *frameHint; // Helps avoid GetPrimaryFrameFor() calls PRInt32 siblingIndex; // Holds a state flag or an index into the siblingList PRBool isHidden; // Don't enter subtree if hidden }; @@ -74,22 +72,26 @@ public: virtual ~nsAccessibleTreeWalker(); NS_IMETHOD GetNextSibling(); + NS_IMETHOD GetPreviousSibling(); NS_IMETHOD GetParent(); NS_IMETHOD GetFirstChild(); + NS_IMETHOD GetLastChild(); WalkState mState; + WalkState mInitialState; protected: + NS_IMETHOD GetChildBefore(nsIDOMNode* aParent, nsIDOMNode* aChild); PRBool GetAccessible(); NS_IMETHOD GetFullTreeParentNode(nsIDOMNode *aChildNode, nsIDOMNode **aParentNodeOut); + void GetSiblings(nsIDOMNode *aOneOfTheSiblings); void GetKids(nsIDOMNode *aParent); void ClearState(); NS_IMETHOD PushState(); NS_IMETHOD PopState(); - void UpdateFrame(PRBool aTryFirstChild); - void GetNextDOMNode(); + void UpdateFrameHint(nsIDOMNode *aStartNode, PRBool aTryFirstChild); nsCOMPtr mWeakShell; nsCOMPtr mAccService; diff --git a/mozilla/accessible/src/html/nsHTMLFormControlAccessible.cpp b/mozilla/accessible/src/html/nsHTMLFormControlAccessible.cpp index 718404f0ad0..f6886577575 100644 --- a/mozilla/accessible/src/html/nsHTMLFormControlAccessible.cpp +++ b/mozilla/accessible/src/html/nsHTMLFormControlAccessible.cpp @@ -475,7 +475,7 @@ void nsHTMLGroupboxAccessible::CacheChildren(PRBool aWalkAnonContent) if (mAccChildCount == eChildCountUninitialized) { nsAccessibleTreeWalker walker(mWeakShell, mDOMNode, aWalkAnonContent); - walker.mState.frame = GetFrame(); + walker.mState.frameHint = GetFrame(); mAccChildCount = 0; walker.GetFirstChild(); // Check for and skip it if it's there diff --git a/mozilla/accessible/src/html/nsHTMLTextAccessible.cpp b/mozilla/accessible/src/html/nsHTMLTextAccessible.cpp index b7842376b5c..1b51cb62213 100644 --- a/mozilla/accessible/src/html/nsHTMLTextAccessible.cpp +++ b/mozilla/accessible/src/html/nsHTMLTextAccessible.cpp @@ -228,7 +228,7 @@ void nsHTMLLIAccessible::CacheChildren(PRBool aWalkAnonContent) SetFirstChild(mBulletAccessible); mAccChildCount = 1; nsAccessibleTreeWalker walker(mWeakShell, mDOMNode, aWalkAnonContent); - walker.mState.frame = GetFrame(); + walker.mState.frameHint = GetFrame(); walker.GetFirstChild(); nsCOMPtr privatePrevAccessible = do_QueryInterface(mBulletAccessible); diff --git a/mozilla/accessible/src/xul/nsXULFormControlAccessible.cpp b/mozilla/accessible/src/xul/nsXULFormControlAccessible.cpp index 69be0d570f3..db1ecce79e8 100644 --- a/mozilla/accessible/src/xul/nsXULFormControlAccessible.cpp +++ b/mozilla/accessible/src/xul/nsXULFormControlAccessible.cpp @@ -163,22 +163,17 @@ NS_IMETHODIMP nsXULButtonAccessible::GetFirstChild(nsIAccessible **aResult) { if (!mFirstChild) { nsAccessibleTreeWalker walker(mWeakShell, mDOMNode, PR_TRUE); - walker.GetFirstChild(); - nsCOMPtr dropMarkerAccessible; - while (walker.mState.accessible) { - dropMarkerAccessible = walker.mState.accessible; - walker.GetNextSibling(); - } + walker.GetLastChild(); // If the anonymous tree walker can find accessible children, // and the last one is a push button, then use it as the only accessible // child -- because this is the scenario where we have a dropmarker child - if (dropMarkerAccessible) { + if (walker.mState.accessible) { PRUint32 role; - if (NS_SUCCEEDED(dropMarkerAccessible->GetRole(&role)) && role == ROLE_PUSHBUTTON) { - mFirstChild = dropMarkerAccessible; - nsCOMPtr privChildAcc = do_QueryInterface(dropMarkerAccessible); + if (NS_SUCCEEDED(walker.mState.accessible->GetRole(&role)) && role == ROLE_PUSHBUTTON) { + mFirstChild = walker.mState.accessible; + nsCOMPtr privChildAcc = do_QueryInterface(mFirstChild); privChildAcc->SetNextSibling(nsnull); } }