зеркало из https://github.com/mozilla/gecko-dev.git
Bug 638684 - Accessible::GetFirstAvailableAccessible needs resuse the accessible tree walker, r=tbsaunde
This commit is contained in:
Родитель
4296818d2c
Коммит
3d35730e1e
|
@ -9,7 +9,7 @@
|
|||
#include "nsAccessibilityService.h"
|
||||
#include "DocAccessible.h"
|
||||
|
||||
#include "nsINodeList.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
|
@ -39,9 +39,9 @@ struct WalkState
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TreeWalker::
|
||||
TreeWalker(Accessible* aContext, nsIContent* aContent, bool aWalkCache) :
|
||||
TreeWalker(Accessible* aContext, nsIContent* aContent, uint32_t aFlags) :
|
||||
mDoc(aContext->Document()), mContext(aContext),
|
||||
mWalkCache(aWalkCache), mState(nullptr)
|
||||
mFlags(aFlags), mState(nullptr)
|
||||
{
|
||||
NS_ASSERTION(aContent, "No node for the accessible tree walker!");
|
||||
|
||||
|
@ -86,7 +86,8 @@ TreeWalker::NextChildInternal(bool aNoWalkUp)
|
|||
mState->childIdx++;
|
||||
|
||||
bool isSubtreeHidden = false;
|
||||
Accessible* accessible = mWalkCache ? mDoc->GetAccessible(childNode) :
|
||||
Accessible* accessible = mFlags & eWalkCache ?
|
||||
mDoc->GetAccessible(childNode) :
|
||||
GetAccService()->GetOrCreateAccessible(childNode, mContext,
|
||||
&isSubtreeHidden);
|
||||
|
||||
|
@ -95,9 +96,7 @@ TreeWalker::NextChildInternal(bool aNoWalkUp)
|
|||
|
||||
// Walk down into subtree to find accessibles.
|
||||
if (!isSubtreeHidden) {
|
||||
if (!PushState(childNode))
|
||||
break;
|
||||
|
||||
PushState(childNode);
|
||||
accessible = NextChildInternal(true);
|
||||
if (accessible)
|
||||
return accessible;
|
||||
|
@ -105,9 +104,42 @@ TreeWalker::NextChildInternal(bool aNoWalkUp)
|
|||
}
|
||||
|
||||
// No more children, get back to the parent.
|
||||
nsIContent* anchorNode = mState->content;
|
||||
PopState();
|
||||
if (aNoWalkUp)
|
||||
return nullptr;
|
||||
|
||||
return aNoWalkUp ? nullptr : NextChildInternal(false);
|
||||
if (mState)
|
||||
return NextChildInternal(false);
|
||||
|
||||
// If we traversed the whole subtree of the anchor node. Move to next node
|
||||
// relative anchor node within the context subtree if possible.
|
||||
if (mFlags != eWalkContextTree)
|
||||
return nullptr;
|
||||
|
||||
while (anchorNode != mContext->GetNode()) {
|
||||
nsINode* parentNode = anchorNode->GetFlattenedTreeParent();
|
||||
if (!parentNode || !parentNode->IsElement())
|
||||
return nullptr;
|
||||
|
||||
PushState(parentNode->AsElement());
|
||||
mState->childList = mState->content->GetChildren(mChildFilter);
|
||||
length = 0;
|
||||
if (mState->childList)
|
||||
mState->childList->GetLength(&length);
|
||||
|
||||
while (mState->childIdx < length) {
|
||||
nsIContent* childNode = mState->childList->Item(mState->childIdx);
|
||||
mState->childIdx++;
|
||||
if (childNode == anchorNode)
|
||||
return NextChildInternal(false);
|
||||
}
|
||||
PopState();
|
||||
|
||||
anchorNode = parentNode->AsElement();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -118,15 +150,10 @@ TreeWalker::PopState()
|
|||
mState = prevToLastState;
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
TreeWalker::PushState(nsIContent* aContent)
|
||||
{
|
||||
WalkState* nextToLastState = new WalkState(aContent);
|
||||
if (!nextToLastState)
|
||||
return false;
|
||||
|
||||
nextToLastState->prevState = mState;
|
||||
mState = nextToLastState;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef mozilla_a11y_TreeWalker_h_
|
||||
#define mozilla_a11y_TreeWalker_h_
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include <stdint.h>
|
||||
|
||||
class nsIContent;
|
||||
|
@ -21,11 +22,26 @@ struct WalkState;
|
|||
/**
|
||||
* This class is used to walk the DOM tree to create accessible tree.
|
||||
*/
|
||||
class TreeWalker
|
||||
class TreeWalker MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
TreeWalker(Accessible* aContext, nsIContent* aNode, bool aWalkCache = false);
|
||||
virtual ~TreeWalker();
|
||||
enum {
|
||||
// used to walk the existing tree of the given node
|
||||
eWalkCache = 1,
|
||||
// used to walk the context tree starting from given node
|
||||
eWalkContextTree = 2 | eWalkCache
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param aContext [in] container accessible for the given node, used to
|
||||
* define accessible context
|
||||
* @param aNode [in] the node the search will be prepared relative to
|
||||
* @param aFlags [in] flags (see enum above)
|
||||
*/
|
||||
TreeWalker(Accessible* aContext, nsIContent* aNode, uint32_t aFlags = 0);
|
||||
~TreeWalker();
|
||||
|
||||
/**
|
||||
* Return the next child accessible.
|
||||
|
@ -59,7 +75,7 @@ private:
|
|||
* @note State stack is used to navigate up/down the DOM subtree during
|
||||
* accessible children search.
|
||||
*/
|
||||
bool PushState(nsIContent *aNode);
|
||||
void PushState(nsIContent* aNode);
|
||||
|
||||
/**
|
||||
* Pop state from stack.
|
||||
|
@ -69,7 +85,7 @@ private:
|
|||
DocAccessible* mDoc;
|
||||
Accessible* mContext;
|
||||
int32_t mChildFilter;
|
||||
bool mWalkCache;
|
||||
uint32_t mFlags;
|
||||
WalkState* mState;
|
||||
};
|
||||
|
||||
|
|
|
@ -3060,40 +3060,6 @@ Accessible::GetSiblingAtOffset(int32_t aOffset, nsresult* aError) const
|
|||
return child;
|
||||
}
|
||||
|
||||
Accessible*
|
||||
Accessible::GetFirstAvailableAccessible(nsINode *aStartNode) const
|
||||
{
|
||||
Accessible* accessible = mDoc->GetAccessible(aStartNode);
|
||||
if (accessible)
|
||||
return accessible;
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = aStartNode->OwnerDoc();
|
||||
|
||||
nsCOMPtr<nsINode> currentNode = aStartNode;
|
||||
ErrorResult rv;
|
||||
nsRefPtr<dom::TreeWalker> walker =
|
||||
doc->CreateTreeWalker(*GetNode(),
|
||||
nsIDOMNodeFilter::SHOW_ELEMENT | nsIDOMNodeFilter::SHOW_TEXT,
|
||||
nullptr, rv);
|
||||
NS_ENSURE_TRUE(walker, nullptr);
|
||||
|
||||
walker->SetCurrentNode(*currentNode, rv);
|
||||
if (rv.Failed())
|
||||
return nullptr;
|
||||
|
||||
while (true) {
|
||||
currentNode = walker->NextNode(rv);
|
||||
if (!currentNode || rv.Failed())
|
||||
return nullptr;
|
||||
|
||||
Accessible* accessible = mDoc->GetAccessible(currentNode);
|
||||
if (accessible)
|
||||
return accessible;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
double
|
||||
Accessible::AttrNumericValue(nsIAtom* aAttr) const
|
||||
{
|
||||
|
|
|
@ -888,16 +888,6 @@ protected:
|
|||
// helper method to verify frames
|
||||
static nsresult GetFullKeyName(const nsAString& aModifierName, const nsAString& aKeyName, nsAString& aStringOut);
|
||||
|
||||
/**
|
||||
* Return an accessible for the given DOM node, or if that node isn't
|
||||
* accessible, return the accessible for the next DOM node which has one
|
||||
* (based on forward depth first search).
|
||||
*
|
||||
* @param aStartNode [in] the DOM node to start from
|
||||
* @return the resulting accessible
|
||||
*/
|
||||
Accessible* GetFirstAvailableAccessible(nsINode* aStartNode) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Action helpers
|
||||
|
||||
|
|
|
@ -1783,7 +1783,7 @@ DocAccessible::UpdateTree(Accessible* aContainer, nsIContent* aChildNode,
|
|||
updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
|
||||
} else {
|
||||
if (aIsInsert) {
|
||||
TreeWalker walker(aContainer, aChildNode, true);
|
||||
TreeWalker walker(aContainer, aChildNode, TreeWalker::eWalkCache);
|
||||
|
||||
while ((child = walker.NextChild()))
|
||||
updateFlags |= UpdateTreeInternal(child, aIsInsert, reorderEvent);
|
||||
|
|
|
@ -302,7 +302,16 @@ HyperTextAccessible::DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
|
|||
// This <br> is the hacky "bogus node" used when there is no text in a control
|
||||
return 0;
|
||||
}
|
||||
descendant = GetFirstAvailableAccessible(findNode);
|
||||
|
||||
descendant = mDoc->GetAccessible(findNode);
|
||||
if (!descendant && findNode->IsContent()) {
|
||||
Accessible* container = mDoc->GetContainerAccessible(findNode);
|
||||
if (container) {
|
||||
TreeWalker walker(container, findNode->AsContent(),
|
||||
TreeWalker::eWalkContextTree);
|
||||
descendant = walker.NextChild();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TransformOffset(descendant, offset, aIsEndOffset);
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5,
|
||||
"hypertext3", kOk, kOk, kOk);
|
||||
|
||||
// XXX: see bug 638684.
|
||||
// XXX: see bug 634202.
|
||||
testTextAtOffset(0, BOUNDARY_LINE_START, "line ", 0, 5,
|
||||
"hypertext4", kTodo, kOk, kTodo);
|
||||
|
||||
|
|
|
@ -109,6 +109,44 @@
|
|||
}
|
||||
}
|
||||
|
||||
function changeDOMSelection(aID, aNodeID1, aNodeOffset1,
|
||||
aNodeID2, aNodeOffset2,
|
||||
aStartOffset, aEndOffset)
|
||||
{
|
||||
this.hyperText = getAccessible(aID, [ nsIAccessibleText ]);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
|
||||
];
|
||||
|
||||
this.invoke = function changeDOMSelection_invoke()
|
||||
{
|
||||
var sel = window.getSelection();
|
||||
var range = document.createRange();
|
||||
range.setStart(getNode(aNodeID1), aNodeOffset1);
|
||||
range.setEnd(getNode(aNodeID2), aNodeOffset2);
|
||||
sel.addRange(range);
|
||||
}
|
||||
|
||||
this.finalCheck = function changeDOMSelection_finalCheck()
|
||||
{
|
||||
is(this.hyperText.selectionCount, 1,
|
||||
"setSelectionBounds: Wrong selection count for " + aID);
|
||||
var startOffset = {}, endOffset = {};
|
||||
this.hyperText.getSelectionBounds(0, startOffset, endOffset);
|
||||
|
||||
is(startOffset.value, aStartOffset,
|
||||
"setSelectionBounds: Wrong start offset for " + aID);
|
||||
is(endOffset.value, aEndOffset,
|
||||
"setSelectionBounds: Wrong end offset for " + aID);
|
||||
}
|
||||
|
||||
this.getID = function changeDOMSelection_getID()
|
||||
{
|
||||
return "DOM selection change for " + aID;
|
||||
}
|
||||
}
|
||||
|
||||
function onfocusEventSeq(aID)
|
||||
{
|
||||
var caretMovedChecker =
|
||||
|
@ -141,6 +179,7 @@
|
|||
gQueue.push(new synthFocus("textarea", onfocusEventSeq("textarea")));
|
||||
gQueue.push(new changeSelection("textarea", 1, 3));
|
||||
|
||||
gQueue.push(new changeDOMSelection("c1", "c1_span1", 0, "c1_span2", 0, 2, 2));
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -169,6 +208,7 @@
|
|||
<p id="paragraph">hello</p>
|
||||
<input id="textbox" value="hello"/>
|
||||
<textarea id="textarea">hello</textarea>
|
||||
<div id="c1">hi<span id="c1_span1"></span><span id="c1_span2"></span>hi</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
Загрузка…
Ссылка в новой задаче