Bug 536344 - posinset and setsize aren't calculated right for the flatter trees, r=marcoz, davidb

This commit is contained in:
Alexander Surkov 2010-01-13 03:07:38 +08:00
parent d55818f5b3
commit 75853371bc
6 changed files with 183 additions and 63 deletions

View File

@ -103,6 +103,46 @@ nsAccUtils::SetAccGroupAttrs(nsIPersistentProperties *aAttributes,
}
}
PRInt32
nsAccUtils::GetDefaultLevel(nsAccessible *aAcc)
{
PRUint32 role = nsAccUtils::Role(aAcc);
if (role == nsIAccessibleRole::ROLE_OUTLINEITEM)
return 1;
if (role == nsIAccessibleRole::ROLE_ROW) {
nsCOMPtr<nsIAccessible> parent = aAcc->GetParent();
if (Role(parent) == nsIAccessibleRole::ROLE_TREE_TABLE) {
// It is a row inside flatten treegrid. Group level is always 1 until it
// is overriden by aria-level attribute.
return 1;
}
}
return 0;
}
PRInt32
nsAccUtils::GetARIAOrDefaultLevel(nsIAccessible *aAcc)
{
nsRefPtr<nsAccessible> acc = nsAccUtils::QueryObject<nsAccessible>(aAcc);
NS_ENSURE_TRUE(acc, 0);
nsCOMPtr<nsIDOMNode> node;
acc->GetDOMNode(getter_AddRefs(node));
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
NS_ENSURE_TRUE(content, 0);
PRInt32 level = 0;
nsCoreUtils::GetUIntAttr(content, nsAccessibilityAtoms::aria_level, &level);
if (level != 0)
return level;
return GetDefaultLevel(acc);
}
void
nsAccUtils::GetPositionAndSizeForXULSelectControlItem(nsIDOMNode *aNode,
PRInt32 *aPosInSet,

View File

@ -92,6 +92,17 @@ public:
PRInt32 aLevel, PRInt32 aSetSize,
PRInt32 aPosInSet);
/**
* Get default value of the level for the given accessible.
*/
static PRInt32 GetDefaultLevel(nsAccessible *aAcc);
/**
* Return ARIA level value or the default one if ARIA is missed for the
* given accessible.
*/
static PRInt32 GetARIAOrDefaultLevel(nsIAccessible *aAcc);
/**
* Compute position in group (posinset) and group size (setsize) for
* nsIDOMXULSelectControlItemElement node.

View File

@ -1644,29 +1644,12 @@ nsAccessible::GroupPosition(PRInt32 *aGroupLevel,
if (!content)
return NS_OK;
nsAutoString value;
PRInt32 error = NS_OK;
content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_level, value);
if (!value.IsEmpty()) {
PRInt32 level = value.ToInteger(&error);
if (NS_SUCCEEDED(error))
*aGroupLevel = level;
}
content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_posinset, value);
if (!value.IsEmpty()) {
PRInt32 posInSet = value.ToInteger(&error);
if (NS_SUCCEEDED(error))
*aPositionInGroup = posInSet;
}
content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_setsize, value);
if (!value.IsEmpty()) {
PRInt32 sizeSet = value.ToInteger(&error);
if (NS_SUCCEEDED(error))
*aSimilarItemsInGroup = sizeSet;
}
nsCoreUtils::GetUIntAttr(content, nsAccessibilityAtoms::aria_level,
aGroupLevel);
nsCoreUtils::GetUIntAttr(content, nsAccessibilityAtoms::aria_posinset,
aPositionInGroup);
nsCoreUtils::GetUIntAttr(content, nsAccessibilityAtoms::aria_setsize,
aSimilarItemsInGroup);
// If ARIA is missed and the accessible is visible then calculate group
// position from hierarchy.
@ -3294,9 +3277,6 @@ nsAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet, PRInt32 *aSetSize)
role != nsIAccessibleRole::ROLE_GRID_CELL)
return;
PRInt32 positionInGroup = 0;
PRInt32 setSize = 0;
PRUint32 baseRole = role;
if (role == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
role == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
@ -3305,36 +3285,70 @@ nsAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet, PRInt32 *aSetSize)
nsAccessible* parent = GetParent();
NS_ENSURE_TRUE(parent,);
PRBool foundCurrent = PR_FALSE;
PRInt32 siblingCount = parent->GetChildCount();
for (PRInt32 siblingIdx = 0; siblingIdx < siblingCount; siblingIdx++) {
nsAccessible* sibling = parent->GetChildAt(siblingIdx);
PRInt32 indexInParent = parent->GetIndexOf(this);
PRInt32 level = nsAccUtils::GetARIAOrDefaultLevel(this);
// Compute 'posinset'.
PRInt32 positionInGroup = 1;
for (PRInt32 idx = indexInParent - 1; idx >= 0; idx--) {
nsAccessible* sibling = parent->GetChildAt(idx);
PRUint32 siblingRole = siblingRole = nsAccUtils::Role(sibling);
// If the sibling is separator then the group is ended.
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
break;
PRUint32 siblingBaseRole = siblingRole;
if (siblingRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
siblingRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
siblingBaseRole = nsIAccessibleRole::ROLE_MENUITEM;
// If sibling is visible and has the same base role.
// If sibling is visible and has the same base role
if (siblingBaseRole == baseRole &&
!(nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)) {
++ setSize;
if (!foundCurrent) {
++ positionInGroup;
if (sibling == this)
foundCurrent = PR_TRUE;
}
}
// If the sibling is separator
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR) {
if (foundCurrent) // the our group is ended
// and check if it's hierarchical flatten structure, i.e. if the sibling
// level is lesser than this one then group is ended, if the sibling level
// is greater than this one then the group is splited by some child
// elements (group will be continued).
PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level)
break;
else if (level == siblingLevel)
++ positionInGroup;
}
}
// not our group, continue the searching
positionInGroup = 0;
setSize = 0;
// Compute 'setsize'.
PRInt32 setSize = positionInGroup;
PRInt32 siblingCount = parent->GetChildCount();
for (PRInt32 idx = indexInParent + 1; idx < siblingCount; idx++) {
nsAccessible* sibling = parent->GetChildAt(idx);
NS_ENSURE_TRUE(sibling,);
PRUint32 siblingRole = nsAccUtils::Role(sibling);
// If the sibling is separator then the group is ended.
if (siblingRole == nsIAccessibleRole::ROLE_SEPARATOR)
break;
PRUint32 siblingBaseRole = siblingRole;
if (siblingRole == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM ||
siblingRole == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM)
siblingBaseRole = nsIAccessibleRole::ROLE_MENUITEM;
// If sibling is visible and has the same base role
if (siblingBaseRole == baseRole &&
!(nsAccUtils::State(sibling) & nsIAccessibleStates::STATE_INVISIBLE)) {
// and check if it's hierarchical flatten structure.
PRInt32 siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
if (siblingLevel < level)
break;
else if (level == siblingLevel)
++ setSize;
}
}
@ -3345,6 +3359,8 @@ nsAccessible::GetPositionAndSizeInternal(PRInt32 *aPosInSet, PRInt32 *aSetSize)
PRInt32
nsAccessible::GetLevelInternal()
{
PRInt32 level = nsAccUtils::GetDefaultLevel(this);
PRUint32 role = nsAccUtils::Role(this);
nsAccessible* parent = GetParent();
@ -3352,7 +3368,8 @@ nsAccessible::GetLevelInternal()
// Always expose 'level' attribute for 'outlineitem' accessible. The number
// of nested 'grouping' accessibles containing 'outlineitem' accessible is
// its level.
PRInt32 level = 1;
level = 1;
while (parent) {
PRUint32 parentRole = nsAccUtils::Role(parent);
@ -3364,17 +3381,14 @@ nsAccessible::GetLevelInternal()
parent = parent->GetParent();
}
return level;
}
if (role == nsIAccessibleRole::ROLE_LISTITEM) {
} else if (role == nsIAccessibleRole::ROLE_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.
// Calculate 'level' attribute based on number of parent listitems.
PRInt32 level = 0;
level = 0;
while (parent) {
PRUint32 parentRole = nsAccUtils::Role(parent);
@ -3405,16 +3419,7 @@ nsAccessible::GetLevelInternal()
} else {
++ level; // level is 1-index based
}
return level;
}
if (role == nsIAccessibleRole::ROLE_ROW &&
nsAccUtils::Role(parent) == nsIAccessibleRole::ROLE_TREE_TABLE) {
// It is a row inside flatten treegrid. Group level is always 1 until it is
// overriden by aria-level attribute.
return 1;
}
return 0;
return level;
}

View File

@ -581,6 +581,23 @@ nsCoreUtils::GetID(nsIContent *aContent, nsAString& aID)
return idAttribute ? aContent->GetAttr(kNameSpaceID_None, idAttribute, aID) : PR_FALSE;
}
PRBool
nsCoreUtils::GetUIntAttr(nsIContent *aContent, nsIAtom *aAttr, PRInt32 *aUInt)
{
nsAutoString value;
aContent->GetAttr(kNameSpaceID_None, aAttr, value);
if (!value.IsEmpty()) {
PRInt32 error = NS_OK;
PRInt32 integer = value.ToInteger(&error);
if (NS_SUCCEEDED(error) && integer > 0) {
*aUInt = integer;
return PR_TRUE;
}
}
return PR_FALSE;
}
PRBool
nsCoreUtils::IsXLink(nsIContent *aContent)
{

View File

@ -250,6 +250,13 @@ public:
*/
static PRBool GetID(nsIContent *aContent, nsAString& aID);
/**
* Convert attribute value of the given node to positive integer. If no
* attribute or wrong value then false is returned.
*/
static PRBool GetUIntAttr(nsIContent *aContent, nsIAtom *aAttr,
PRInt32 *aUInt);
/**
* Check if the given element is XLink.
*

View File

@ -97,6 +97,17 @@
testGroupAttrs("r2", 2, 3);
testGroupAttrs("r3", 3, 3);
//////////////////////////////////////////////////////////////////////////
// ARIA tree
testGroupAttrs("ti1", 1, 3, 1);
testGroupAttrs("ti2", 1, 2, 2);
testGroupAttrs("ti3", 2, 2, 2);
testGroupAttrs("ti4", 2, 3, 1);
testGroupAttrs("ti5", 1, 3, 2);
testGroupAttrs("ti6", 2, 3, 2);
testGroupAttrs("ti7", 3, 3, 2);
testGroupAttrs("ti8", 3, 3, 1);
//////////////////////////////////////////////////////////////////////////
// ARIA grid
testGroupAttrs("grid_row1", 1, 2);
@ -109,15 +120,15 @@
//////////////////////////////////////////////////////////////////////////
// ARIA treegrid
testGroupAttrs("treegrid_row1", 1, 3, 1);
testGroupAttrs("treegrid_row1", 1, 2, 1);
testGroupAttrs("treegrid_cell1", 1, 2);
testGroupAttrs("treegrid_cell2", 2, 2);
testGroupAttrs("treegrid_row2", 2, 3, 2);
testGroupAttrs("treegrid_row2", 1, 1, 2);
testGroupAttrs("treegrid_cell3", 1, 2);
testGroupAttrs("treegrid_cell4", 2, 2);
testGroupAttrs("treegrid_row3", 3, 3, 1);
testGroupAttrs("treegrid_row3", 2, 2, 1);
testGroupAttrs("treegrid_cell5", 1, 2);
testGroupAttrs("treegrid_cell6", 2, 2);
@ -234,6 +245,35 @@
<li id="r3" role="radio" aria-checked="false">Jimmy Johns</li>
</ul>
<table role="tree">
<tr role="presentation">
<td role="treeitem" aria-expanded="true" aria-level="1"
id="ti1">vegetables</td>
</tr>
<tr role="presentation">
<td role="treeitem" aria-level="2" id="ti2">cucumber</td>
</tr>
<tr role="presentation">
<td role="treeitem" aria-level="2" id="ti3">carrot</td>
</tr>
<tr role="presentation">
<td role="treeitem" aria-expanded="false" aria-level="1"
id="ti4">cars</td>
</tr>
<tr role="presentation">
<td role="treeitem" aria-level="2" id="ti5">mercedes</td>
</tr>
<tr role="presentation">
<td role="treeitem" aria-level="2" id="ti6">BMW</td>
</tr>
<tr role="presentation">
<td role="treeitem" aria-level="2" id="ti7">Audi</td>
</tr>
<tr role="presentation">
<td role="treeitem" aria-level="1" id="ti8">people</td>
</tr>
</table>
<table role="grid">
<tr role="row" id="grid_row1">
<td role="gridcell" id="grid_cell1">cell1</td>