Bug 864224 - Support nested ARIA listitems structured by role='group', r=tbsaunde

This commit is contained in:
Alexander Surkov 2013-05-16 15:38:17 +09:00
parent 937aa4fe6a
commit 321c3993eb
4 changed files with 175 additions and 37 deletions

View File

@ -110,52 +110,69 @@ AccGroupInfo::AccGroupInfo(Accessible* aItem, role aRole) :
if (IsConceptualParent(aRole, parentRole))
mParent = parent;
// In the case of ARIA tree (not ARIA treegrid) a tree can be arranged by
// using ARIA groups to organize levels. In this case the parent of the tree
// item will be a group and the previous treeitem of that should be the tree
// item parent.
if (parentRole != roles::GROUPING || aRole != roles::OUTLINEITEM)
// ARIA tree and list can be arranged by using ARIA groups to organize levels.
if (parentRole != roles::GROUPING)
return;
Accessible* parentPrevSibling = parent->PrevSibling();
if (!parentPrevSibling)
return;
roles::Role parentPrevSiblingRole = parentPrevSibling->Role();
if (parentPrevSiblingRole == roles::TEXT_LEAF) {
// XXX Sometimes an empty text accessible is in the hierarchy here,
// although the text does not appear to be rendered, GetRenderedText()
// says that it is so we need to skip past it to find the true
// previous sibling.
parentPrevSibling = parentPrevSibling->PrevSibling();
if (parentPrevSibling)
parentPrevSiblingRole = parentPrevSibling->Role();
// Way #1 for ARIA tree (not ARIA treegrid): previous sibling of a group is a
// parent. In other words the parent of the tree item will be a group and
// the previous tree item of the group is a conceptual parent of the tree
// item.
if (aRole == roles::OUTLINEITEM) {
Accessible* parentPrevSibling = parent->PrevSibling();
if (parentPrevSibling && parentPrevSibling->Role() == aRole) {
mParent = parentPrevSibling;
return;
}
}
// Previous sibling of parent group is a tree item, this is the
// conceptual tree item parent.
if (parentPrevSiblingRole == roles::OUTLINEITEM)
mParent = parentPrevSibling;
// Way #2 for ARIA list and tree: group is a child of an item. In other words
// the parent of the item will be a group and containing item of the group is
// a conceptual parent of the item.
if (aRole == roles::LISTITEM || aRole == roles::OUTLINEITEM) {
Accessible* grandParent = parent->Parent();
if (grandParent && grandParent->Role() == aRole)
mParent = grandParent;
}
}
Accessible*
AccGroupInfo::FirstItemOf(Accessible* aContainer)
{
// ARIA trees can be arranged by ARIA groups, otherwise aria-level works.
// ARIA tree can be arranged by ARIA groups case #1 (previous sibling of a
// group is a parent) or by aria-level.
a11y::role containerRole = aContainer->Role();
Accessible* item = aContainer->NextSibling();
if (item) {
if (containerRole == roles::OUTLINEITEM && item->Role() == roles::GROUPING)
item = item->FirstChild();
AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
return item;
if (item) {
AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
return item;
}
}
// ARIA list and tree can be arranged by ARIA groups case #2 (group is
// a child of an item).
item = aContainer->LastChild();
if (!item)
return nullptr;
if (item->Role() == roles::GROUPING &&
(containerRole == roles::LISTITEM || containerRole == roles::OUTLINEITEM)) {
item = item->FirstChild();
if (item) {
AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
return item;
}
}
// Otherwise it can be a direct child.
item = aContainer->FirstChild();
if (item && IsConceptualParent(BaseRole(item->Role()), containerRole))
if (IsConceptualParent(BaseRole(item->Role()), containerRole))
return item;
return nullptr;

View File

@ -2024,7 +2024,8 @@ Accessible::RelationByType(uint32_t aType)
// This is an ARIA tree or treegrid that doesn't use owns, so we need to
// get the parent the hard way.
if (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM ||
if (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM ||
mRoleMapEntry->role == roles::LISTITEM ||
mRoleMapEntry->role == roles::ROW)) {
rel.AppendTarget(GetGroupInfo()->ConceptualParent());
}
@ -2055,8 +2056,10 @@ Accessible::RelationByType(uint32_t aType)
// also can be organized by groups.
if (mRoleMapEntry &&
(mRoleMapEntry->role == roles::OUTLINEITEM ||
mRoleMapEntry->role == roles::LISTITEM ||
mRoleMapEntry->role == roles::ROW ||
mRoleMapEntry->role == roles::OUTLINE ||
mRoleMapEntry->role == roles::LIST ||
mRoleMapEntry->role == roles::TREE_TABLE)) {
rel.AppendIter(new ItemIterator(this));
}
@ -3256,10 +3259,11 @@ Accessible::GetLevelInternal()
}
} else if (role == roles::LISTITEM) {
// Expose 'level' attribute on nested lists. We assume nested list is a last
// child of listitem of parent list. We don't handle the case when nested
// lists have more complex structure, for example when there are accessibles
// between parent listitem and nested list.
// Expose 'level' attribute on nested lists. We support two hierarchies:
// a) list -> listitem -> list -> listitem (nested list is a last child
// of listitem of the parent list);
// b) list -> listitem -> group -> listitem (nested listitems are contained
// by group that is a last child of the parent listitem).
// Calculate 'level' attribute based on number of parent listitems.
level = 0;
@ -3269,9 +3273,8 @@ Accessible::GetLevelInternal()
if (parentRole == roles::LISTITEM)
++ level;
else if (parentRole != roles::LIST)
else if (parentRole != roles::LIST && parentRole != roles::GROUPING)
break;
}
if (level == 0) {
@ -3283,8 +3286,11 @@ Accessible::GetLevelInternal()
Accessible* sibling = parent->GetChildAt(siblingIdx);
Accessible* siblingChild = sibling->LastChild();
if (siblingChild && siblingChild->Role() == roles::LIST)
return 1;
if (siblingChild) {
roles::Role lastChildRole = siblingChild->Role();
if (lastChildRole == roles::LIST || lastChildRole == roles::GROUPING)
return 1;
}
}
} else {
++ level; // level is 1-index based

View File

@ -68,7 +68,7 @@
testGroupAttrs("li9", 3, 3);
//////////////////////////////////////////////////////////////////////////
// ARIA list (nested lists)
// ARIA list (nested lists: list -> listitem -> list -> listitem)
testGroupAttrs("li10", 1, 3, 1);
testGroupAttrs("li11", 2, 3, 1);
testGroupAttrs("li12", 3, 3, 1);
@ -77,6 +77,15 @@
testGroupAttrs("n_li11", 2, 3, 2);
testGroupAttrs("n_li12", 3, 3, 2);
//////////////////////////////////////////////////////////////////////////
// ARIA list (nested lists: list -> listitem -> group -> listitem)
testGroupAttrs("lgt_li1", 1, 2, 1);
testGroupAttrs("lgt_li1_nli1", 1, 2, 2);
testGroupAttrs("lgt_li1_nli2", 2, 2, 2);
testGroupAttrs("lgt_li2", 2, 2, 1);
testGroupAttrs("lgt_li2_nli1", 1, 2, 2);
testGroupAttrs("lgt_li2_nli2", 2, 2, 2);
//////////////////////////////////////////////////////////////////////////
// ARIA menu (menuitem, separator, menuitemradio and menuitemcheckbox)
testGroupAttrs("menu_item1", 1, 2);
@ -110,6 +119,24 @@
testGroupAttrs("ti7", 3, 3, 2);
testGroupAttrs("ti8", 3, 3, 1);
//////////////////////////////////////////////////////////////////////////
// ARIA tree (tree -> treeitem -> group -> treeitem)
testGroupAttrs("tree2_ti1", 1, 2, 1);
testGroupAttrs("tree2_ti1a", 1, 2, 2);
testGroupAttrs("tree2_ti1b", 2, 2, 2);
testGroupAttrs("tree2_ti2", 2, 2, 1);
testGroupAttrs("tree2_ti2a", 1, 2, 2);
testGroupAttrs("tree2_ti2b", 2, 2, 2);
//////////////////////////////////////////////////////////////////////////
// ARIA tree (tree -> treeitem, group -> treeitem)
testGroupAttrs("tree3_ti1", 1, 2, 1);
testGroupAttrs("tree3_ti1a", 1, 2, 2);
testGroupAttrs("tree3_ti1b", 2, 2, 2);
testGroupAttrs("tree3_ti2", 2, 2, 1);
testGroupAttrs("tree3_ti2a", 1, 2, 2);
testGroupAttrs("tree3_ti2b", 2, 2, 2);
//////////////////////////////////////////////////////////////////////////
// ARIA grid
testGroupAttrs("grid_row1", 1, 2);
@ -164,6 +191,11 @@
title="Expose level for nested lists in HTML">
Mozilla Bug 468418
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=864224"
title="Support nested ARIA listitems structured by role='group'">
Bug 864224
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
@ -228,6 +260,21 @@
</span>
</span>
<div role="list">
<div role="listitem" id="lgt_li1">Item 1
<div role="group">
<div role="listitem" id="lgt_li1_nli1">Item 1A</div>
<div role="listitem" id="lgt_li1_nli2">Item 1B</div>
</div>
</div>
<div role="listitem" id="lgt_li2">Item 2
<div role="group">
<div role="listitem" id="lgt_li2_nli1">Item 2A</div>
<div role="listitem" id="lgt_li2_nli2">Item 2B</div>
</div>
</div>
</div>
<ul role="menubar">
<li role="menuitem" aria-haspopup="true" id="menu_item1">File
<ul role="menu">
@ -283,6 +330,34 @@
</tr>
</table>
<ul role="tree">
<li role="treeitem" id="tree2_ti1">Item 1
<ul role="group">
<li role="treeitem" id="tree2_ti1a">Item 1A</li>
<li role="treeitem" id="tree2_ti1b">Item 1B</li>
</ul>
</li>
<li role="treeitem" id="tree2_ti2">Item 2
<ul role="group">
<li role="treeitem" id="tree2_ti2a">Item 2A</li>
<li role="treeitem" id="tree2_ti2b">Item 2B</li>
</ul>
</li>
</div>
<div role="tree">
<div role="treeitem" id="tree3_ti1">Item 1</div>
<div role="group">
<li role="treeitem" id="tree3_ti1a">Item 1A</li>
<li role="treeitem" id="tree3_ti1b">Item 1B</li>
</div>
<div role="treeitem" id="tree3_ti2">Item 2</div>
<div role="group">
<div role="treeitem" id="tree3_ti2a">Item 2A</div>
<div role="treeitem" id="tree3_ti2b">Item 2B</div>
</div>
</div>
<table role="grid">
<tr role="row" id="grid_row1">
<td role="gridcell" id="grid_cell1">cell1</td>

View File

@ -73,12 +73,20 @@
testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
testRelation("treeitem6", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem7", RELATION_NODE_CHILD_OF, "treeitem6");
testRelation("tree2_ti1", RELATION_NODE_CHILD_OF, "tree2");
testRelation("tree2_ti1a", RELATION_NODE_CHILD_OF, "tree2_ti1");
testRelation("tree2_ti1b", RELATION_NODE_CHILD_OF, "tree2_ti1");
// 'node child of' relation for row role of treegrid
testRelation("treegridrow1", RELATION_NODE_CHILD_OF, "treegrid");
testRelation("treegridrow2", RELATION_NODE_CHILD_OF, "treegrid");
testRelation("treegridrow3", RELATION_NODE_CHILD_OF, "treegridrow2");
// 'node child of' relation for lists organized by groups
testRelation("listitem1", RELATION_NODE_CHILD_OF, "list");
testRelation("listitem1.1", RELATION_NODE_CHILD_OF, "listitem1");
testRelation("listitem1.2", RELATION_NODE_CHILD_OF, "listitem1");
// 'node child of' relation for the document having window, returns
// direct accessible parent (fixed in bug 419770).
var iframeElmObj = {};
@ -95,11 +103,20 @@
"treeitem5"); // aria-level
testRelation("treeitem6", RELATION_NODE_PARENT_OF,
"treeitem7"); // // group role
testRelation("tree2", RELATION_NODE_PARENT_OF, "tree2_ti1"); // group role
testRelation("tree2_ti1", RELATION_NODE_PARENT_OF,
["tree2_ti1a", "tree2_ti1b"]); // group role
testRelation("treegridrow2", RELATION_NODE_PARENT_OF, "treegridrow3");
testRelation("treegrid", RELATION_NODE_PARENT_OF,
["treegridrow1", "treegridrow2"]);
// 'node parent of' relation on ARIA list structured by groups
testRelation("list", RELATION_NODE_PARENT_OF,
"listitem1");
testRelation("listitem1", RELATION_NODE_PARENT_OF,
[ "listitem1.1", "listitem1.2" ]);
// aria-controls
getAccessible("tab");
todo(false,
@ -178,6 +195,11 @@
title="HTML select options gets relation from containing label">
Bug 687393
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=864224"
title="Support nested ARIA listitems structured by role='group'">
Bug 864224
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@ -253,6 +275,15 @@
</div>
</div>
<ul role="tree" id="tree2">
<li role="treeitem" id="tree2_ti1">Item 1
<ul role="group">
<li role="treeitem" id="tree2_ti1a">Item 1A</li>
<li role="treeitem" id="tree2_ti1b">Item 1B</li>
</ul>
</li>
</ul>
<div role="treegrid" id="treegrid">
<div role="row" id="treegridrow1">
<span role="gridcell">cell1</span><span role="gridcell">cell2</span>
@ -265,6 +296,15 @@
</div>
</div>
<div role="list" id="list">
<div role="listitem" id="listitem1">Item 1
<div role="group">
<div role="listitem" id="listitem1.1">Item 1A</div>
<div role="listitem" id="listitem1.2">Item 1B</div>
</div>
</div>
</div>
<iframe id="iframe"></iframe>
<div id="tablist" role="tablist">