Bug 1248838 - ARIA owns change may fail, r=yzen

This commit is contained in:
Alexander Surkov 2016-02-19 13:11:33 -05:00
parent 1139f832a4
commit 0b5d5b3e24
5 changed files with 119 additions and 9 deletions

View File

@ -2130,6 +2130,51 @@ Accessible::RemoveChild(Accessible* aChild)
return true;
}
void
Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
{
MOZ_ASSERT(aChild, "No child was given");
MOZ_ASSERT(aChild->mParent == this, "A child from different subtree was given");
MOZ_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given");
MOZ_ASSERT(static_cast<uint32_t>(aChild->mIndexInParent) != aNewIndex,
"No move, same index");
MOZ_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
#ifdef DEBUG
// AutoTreeMutation should update group info.
AssertInMutatingSubtree();
#endif
mEmbeddedObjCollector = nullptr;
mChildren.RemoveElementAt(aChild->mIndexInParent);
uint32_t startIdx = aNewIndex, endIdx = aChild->mIndexInParent;
// If the child is moved after its current position.
if (static_cast<uint32_t>(aChild->mIndexInParent) < aNewIndex) {
startIdx = aChild->mIndexInParent;
if (aNewIndex == mChildren.Length() + 1) {
// The child is moved to the end.
mChildren.AppendElement(aChild);
endIdx = mChildren.Length() - 1;
}
else {
mChildren.InsertElementAt(aNewIndex - 1, aChild);
endIdx = aNewIndex;
}
}
else {
// The child is moved prior its current position.
mChildren.InsertElementAt(aNewIndex, aChild);
}
for (uint32_t idx = startIdx; idx <= endIdx; idx++) {
mChildren[idx]->mIndexInParent = idx;
mChildren[idx]->mInt.mIndexOfEmbeddedChild = -1;
}
}
Accessible*
Accessible::GetChildAt(uint32_t aIndex) const
{

View File

@ -396,6 +396,11 @@ public:
virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild);
virtual bool RemoveChild(Accessible* aChild);
/**
* Reallocates the child withing its parent.
*/
void MoveChild(uint32_t aNewIndex, Accessible* aChild);
//////////////////////////////////////////////////////////////////////////////
// Accessible tree traverse methods

View File

@ -2058,7 +2058,10 @@ DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
}
if (child->Parent() == aOwner) {
MoveChild(child, insertIdx - 1);
if (child->IsRelocated()) {
children->RemoveElement(child);
}
MoveChild(child, insertIdx);
children->InsertElementAt(arrayIdx, child);
arrayIdx++;
@ -2141,9 +2144,7 @@ DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
reorderEvent->AddSubMutationEvent(hideEvent);
AutoTreeMutation mut(parent);
parent->RemoveChild(aChild);
parent->InsertChildAt(aIdxInParent, aChild);
parent->MoveChild(aIdxInParent, aChild);
aChild->SetRelocated(true);
FireDelayedEvent(hideEvent);
@ -2174,6 +2175,7 @@ DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(child, false);
reorderEvent->AddSubMutationEvent(hideEvent);
FireDelayedEvent(hideEvent);
{
AutoTreeMutation mut(owner);
@ -2181,7 +2183,6 @@ DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
child->SetRelocated(false);
}
FireDelayedEvent(hideEvent);
MaybeNotifyOfValueChange(owner);
FireDelayedEvent(reorderEvent);
}

View File

@ -110,6 +110,27 @@ function isLogged(aModule)
return gAccRetrieval.isLogged(aModule);
}
/**
* Dumps the accessible tree into console.
*/
function dumpTree(aId, aMsg)
{
function dumpTreeIntl(acc, indent)
{
dump(indent + prettyName(acc) + "\n");
var children = acc.children;
for (var i = 0; i < children.length; i++) {
var child = children.queryElementAt(i, nsIAccessible);
dumpTreeIntl(child, indent + " ");
}
}
dump(aMsg + "\n");
var root = getAccessible(aId);
dumpTreeIntl(root, " ");
}
/**
* Invokes the given function when document is loaded and focused. Preferable
* to mochitests 'addLoadEvent' function -- additionally ensures state of the

View File

@ -23,19 +23,21 @@
// Invokers
////////////////////////////////////////////////////////////////////////////
function removeARIAOwns()
function changeARIAOwns()
{
this.eventSeq = [
new invokerChecker(EVENT_HIDE, getNode("t1_checkbox")),
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
new invokerChecker(EVENT_HIDE, getNode("t1_checkbox")),
new invokerChecker(EVENT_SHOW, getNode("t1_checkbox")),
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
];
this.invoke = function removeARIAOwns_invoke()
this.invoke = function setARIAOwns_invoke()
{
// children are swapped
// children are swapped by ARIA owns
var tree =
{ SECTION: [
{ CHECKBUTTON: [
@ -45,6 +47,41 @@
] };
testAccessibleTree("t1_container", tree);
getNode("t1_container").
setAttribute("aria-owns", "t1_button t1_subdiv");
}
this.finalCheck = function setARIAOwns_finalCheck()
{
// children are swapped again, button and subdiv are appended to
// the children.
var tree =
{ SECTION: [
{ CHECKBUTTON: [ ] }, // checkbox, native order
{ PUSHBUTTON: [ ] }, // button, rearranged by ARIA own
{ SECTION: [ ] } // subdiv from the subtree, ARIA owned
] };
testAccessibleTree("t1_container", tree);
}
this.getID = function setARIAOwns_getID()
{
return "Change @aria-owns attribute";
}
}
function removeARIAOwns()
{
this.eventSeq = [
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
];
this.invoke = function removeARIAOwns_invoke()
{
getNode("t1_container").removeAttribute("aria-owns");
}
@ -418,6 +455,7 @@
gQueue = new eventQueue();
// test1
gQueue.push(new changeARIAOwns());
gQueue.push(new removeARIAOwns());
gQueue.push(new setARIAOwns());
gQueue.push(new addIdToARIAOwns());