зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1205318 - make aria-owns loop alg more sophisticated, r=yzen
This commit is contained in:
Родитель
8748ab96f1
Коммит
626112a60a
|
@ -1613,34 +1613,21 @@ DocAccessible::AddDependentIDsFor(Accessible* aRelProvider, nsIAtom* aRelAttr)
|
|||
mInvalidationList.AppendElement(dependentContent);
|
||||
}
|
||||
|
||||
// Update ARIA owns cache.
|
||||
if (relAttr == nsGkAtoms::aria_owns) {
|
||||
// Dependent content cannot point to other aria-owns content or
|
||||
// their parents. Ignore it if so.
|
||||
// XXX: note, this alg may make invalid the scenario when X owns Y
|
||||
// and Y owns Z, we should have something smarter to handle that.
|
||||
bool isvalid = true;
|
||||
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
|
||||
Accessible* owner = it.Key();
|
||||
nsIContent* parentEl = owner->GetContent();
|
||||
while (parentEl && parentEl != dependentContent) {
|
||||
parentEl = parentEl->GetParent();
|
||||
}
|
||||
if (parentEl) {
|
||||
isvalid = false;
|
||||
break;
|
||||
}
|
||||
// ARIA owns cannot refer to itself or a parent. Ignore
|
||||
// the element if so.
|
||||
nsIContent* parentEl = relProviderEl;
|
||||
while (parentEl && parentEl != dependentContent) {
|
||||
parentEl = parentEl->GetParent();
|
||||
}
|
||||
if (isvalid) {
|
||||
// ARIA owns also cannot refer to itself or a parent.
|
||||
nsIContent* parentEl = relProviderEl;
|
||||
while (parentEl && parentEl != dependentContent) {
|
||||
parentEl = parentEl->GetParent();
|
||||
}
|
||||
if (parentEl) {
|
||||
isvalid = false;
|
||||
}
|
||||
|
||||
if (isvalid) {
|
||||
if (!parentEl) {
|
||||
// ARIA owns element cannot refer to an element in parents chain
|
||||
// of other ARIA owns element (including that ARIA owns element)
|
||||
// if it's inside of a dependent element subtree of that
|
||||
// ARIA owns element. Applied recursively.
|
||||
if (!IsInARIAOwnsLoop(relProviderEl, dependentContent)) {
|
||||
nsTArray<nsIContent*>* list =
|
||||
mARIAOwnsHash.LookupOrAdd(aRelProvider);
|
||||
list->AppendElement(dependentContent);
|
||||
|
@ -1759,6 +1746,45 @@ DocAccessible::RemoveDependentIDsFor(Accessible* aRelProvider,
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
DocAccessible::IsInARIAOwnsLoop(nsIContent* aOwnerEl, nsIContent* aDependentEl)
|
||||
{
|
||||
// ARIA owns element cannot refer to an element in parents chain of other ARIA
|
||||
// owns element (including that ARIA owns element) if it's inside of
|
||||
// a dependent element subtree of that ARIA owns element.
|
||||
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
|
||||
Accessible* otherOwner = it.Key();
|
||||
nsIContent* parentEl = otherOwner->GetContent();
|
||||
while (parentEl && parentEl != aDependentEl) {
|
||||
parentEl = parentEl->GetParent();
|
||||
}
|
||||
|
||||
// The dependent element of this ARIA owns element contains some other ARIA
|
||||
// owns element, make sure this ARIA owns element is not in a subtree of
|
||||
// a dependent element of that other ARIA owns element. If not then
|
||||
// continue a check recursively.
|
||||
if (parentEl) {
|
||||
nsTArray<nsIContent*>* childEls = it.UserData();
|
||||
for (uint32_t idx = 0; idx < childEls->Length(); idx++) {
|
||||
nsIContent* childEl = childEls->ElementAt(idx);
|
||||
nsIContent* parentEl = aOwnerEl;
|
||||
while (parentEl && parentEl != childEl) {
|
||||
parentEl = parentEl->GetParent();
|
||||
}
|
||||
if (parentEl) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsInARIAOwnsLoop(aOwnerEl, childEl)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
|
||||
nsIAtom* aAttribute)
|
||||
|
|
|
@ -436,6 +436,12 @@ protected:
|
|||
void RemoveDependentIDsFor(Accessible* aRelProvider,
|
||||
nsIAtom* aRelAttr = nullptr);
|
||||
|
||||
/**
|
||||
* Return true if given ARIA owner element and its referred content make
|
||||
* the loop closed.
|
||||
*/
|
||||
bool IsInARIAOwnsLoop(nsIContent* aOwnerEl, nsIContent* aDependentEl);
|
||||
|
||||
/**
|
||||
* Update or recreate an accessible depending on a changed attribute.
|
||||
*
|
||||
|
|
|
@ -454,11 +454,11 @@ function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags)
|
|||
|
||||
if (accTree.children.length != childCount) {
|
||||
for (var i = 0; i < Math.max(accTree.children.length, childCount); i++) {
|
||||
var accChild;
|
||||
var accChild = null, testChild = null;
|
||||
try {
|
||||
testChild = accTree.children[i];
|
||||
accChild = children.queryElementAt(i, nsIAccessible);
|
||||
|
||||
testChild = accTree.children[i];
|
||||
if (!testChild) {
|
||||
ok(false, prettyName(acc) + " has an extra child at index " + i +
|
||||
" : " + prettyName(accChild));
|
||||
|
@ -481,10 +481,10 @@ function testAccessibleTree(aAccOrElmOrID, aAccTree, aFlags)
|
|||
}
|
||||
info("Matching " + prettyName(accTree) + " and " + prettyName(acc) +
|
||||
" child at index " + i + " : " + prettyName(accChild));
|
||||
|
||||
} catch (e) {
|
||||
ok(false, prettyName(accTree) + " has an extra child at index " + i +
|
||||
ok(false, prettyName(accTree) + " is expected to have a child at index " + i +
|
||||
" : " + prettyName(testChild) + ", " + e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -10,6 +10,7 @@ skip-if = true # Bug 561508
|
|||
[test_aria_imgmap.html]
|
||||
[test_aria_list.html]
|
||||
[test_aria_menu.html]
|
||||
[test_aria_owns.html]
|
||||
[test_aria_presentation.html]
|
||||
[test_aria_table.html]
|
||||
[test_brokencontext.html]
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>@aria-owns attribute testing</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../role.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Tests
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//enableLogging("tree"); // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
function doTest()
|
||||
{
|
||||
var tree =
|
||||
{ SECTION: [ // t1_1
|
||||
{ SECTION: [ // t1_2
|
||||
// no kids, no loop
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("t1_1", tree);
|
||||
|
||||
tree =
|
||||
{ SECTION: [ // t2_1
|
||||
{ SECTION: [ // t2_2
|
||||
{ SECTION: [ // t2_3
|
||||
// no kids, no loop
|
||||
] }
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("t2_1", tree);
|
||||
|
||||
tree =
|
||||
{ SECTION: [ // t3_3
|
||||
{ SECTION: [ // t3_1
|
||||
{ SECTION: [ // t3_2
|
||||
{ SECTION: [ // DOM child of t3_2
|
||||
// no kids, no loop
|
||||
] }
|
||||
] }
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("t3_3", tree);
|
||||
|
||||
tree =
|
||||
{ SECTION: [ // t4_1
|
||||
{ SECTION: [ // DOM child of t4_1
|
||||
// no kids, no loop
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("t4_1", tree);
|
||||
|
||||
tree =
|
||||
{ SECTION: [ // t5_1
|
||||
{ SECTION: [ // DOM child of t5_1
|
||||
{ SECTION: [ // t5_2
|
||||
{ SECTION: [ // DOM child of t5_2
|
||||
{ SECTION: [ // t5_3
|
||||
{ SECTION: [ // DOM child of t5_3
|
||||
// no kids, no loop
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
] }
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("t5_1", tree);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="t1_1" aria-owns="t1_2"></div>
|
||||
<div id="t1_2" aria-owns="t1_1"></div>
|
||||
|
||||
<div id="t2_2" aria-owns="t2_3"></div>
|
||||
<div id="t2_1" aria-owns="t2_2"></div>
|
||||
<div id="t2_3" aria-owns="t2_1"></div>
|
||||
|
||||
<div id="t3_1" aria-owns="t3_2"></div>
|
||||
<div id="t3_2">
|
||||
<div aria-owns="t3_3"></div>
|
||||
</div>
|
||||
<div id="t3_3" aria-owns="t3_1"></div>
|
||||
|
||||
<div id="t4_1"><div aria-owns="t4_1"></div></div>
|
||||
|
||||
<div id="t5_1"><div aria-owns="t5_2"></div>
|
||||
<div id="t5_2"><div aria-owns="t5_3"></div></div>
|
||||
<div id="t5_3"><div aria-owns="t5_1"></div></div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -172,25 +172,13 @@
|
|||
// Test
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
gA11yEventDumpToConsole = true;
|
||||
enableLogging("tree"); // debug stuff
|
||||
//gA11yEventDumpToConsole = true;
|
||||
//enableLogging("tree"); // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
function doTest()
|
||||
{
|
||||
// nested and recursive aria-owns
|
||||
var tree =
|
||||
{ SECTION: [ // container
|
||||
{ SECTION: [ // child
|
||||
{ SECTION: [ // mid div
|
||||
{ SECTION: [] } // grandchild
|
||||
] }
|
||||
] }
|
||||
] };
|
||||
testAccessibleTree("container", tree);
|
||||
|
||||
// dynamic tests
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new removeARIAOwns());
|
||||
|
@ -214,12 +202,6 @@
|
|||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<div id="container" aria-owns="child" aria-label="container"></div>
|
||||
<div id="child" aria-label="child">
|
||||
<div aria-owns="grandchild" aria-label="midchild"></div>
|
||||
</div>
|
||||
<div id="grandchild" aria-owns="container" aria-label="grandchild"></div>
|
||||
|
||||
<div id="container2" aria-owns="t2_checkbox t2_button">
|
||||
<div role="button" id="t2_button"></div>
|
||||
<div role="checkbox" id="t2_checkbox">
|
||||
|
|
Загрузка…
Ссылка в новой задаче