Bug 666212 - summary attribute content mapped to accessible name in MSAA, r=marcoz

This commit is contained in:
Alexander Surkov 2011-06-24 13:01:45 +09:00
parent 1c939adf6e
commit 04a98c120f
7 changed files with 181 additions and 65 deletions

View File

@ -519,7 +519,7 @@ PRUint32 nsTextEquivUtils::gRoleToNameRulesMap[] =
eNoRule, // ROLE_AUTOCOMPLETE
eNoRule, // ROLE_EDITBAR
eFromValue, // ROLE_ENTRY
eNoRule, // ROLE_CAPTION
eFromSubtreeIfRec, // ROLE_CAPTION
eNoRule, // ROLE_DOCUMENT_FRAME
eFromSubtreeIfRec, // ROLE_HEADING
eNoRule, // ROLE_PAGE

View File

@ -464,10 +464,22 @@ nsresult
nsHTMLTableAccessible::GetNameInternal(nsAString& aName)
{
nsAccessible::GetNameInternal(aName);
if (!aName.IsEmpty())
return NS_OK;
if (aName.IsEmpty())
mContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::summary, aName);
// Use table caption as a name.
nsAccessible* caption = Caption();
if (caption) {
nsIContent* captionContent = caption->GetContent();
if (captionContent) {
nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName);
if (!aName.IsEmpty())
return NS_OK;
}
}
// If no caption then use summary as a name.
mContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::summary, aName);
return NS_OK;
}
@ -499,11 +511,8 @@ nsHTMLTableAccessible::GetRelationByType(PRUint32 aRelationType,
aRelation);
NS_ENSURE_SUCCESS(rv, rv);
if (aRelationType == nsIAccessibleRelation::RELATION_DESCRIBED_BY) {
nsCOMPtr<nsIAccessible> accCaption;
GetCaption(getter_AddRefs(accCaption));
return nsRelUtils::AddTarget(aRelationType, aRelation, accCaption);
}
if (aRelationType == nsIAccessibleRelation::RELATION_DESCRIBED_BY)
return nsRelUtils::AddTarget(aRelationType, aRelation, Caption());
return NS_OK;
}
@ -514,10 +523,9 @@ nsHTMLTableAccessible::GetRelationByType(PRUint32 aRelationType,
NS_IMETHODIMP
nsHTMLTableAccessible::GetCaption(nsIAccessible **aCaption)
{
nsAccessible* firstChild = GetChildAt(0);
if (firstChild && firstChild->Role() == nsIAccessibleRole::ROLE_CAPTION)
NS_ADDREF(*aCaption = firstChild);
NS_ENSURE_ARG_POINTER(aCaption);
NS_IF_ADDREF(*aCaption = Caption());
return NS_OK;
}
@ -1268,17 +1276,23 @@ nsHTMLTableAccessible::Description(nsString& aDescription)
if (!aDescription.IsEmpty())
return;
nsCOMPtr<nsIAccessible> captionAccessible;
GetCaption(getter_AddRefs(captionAccessible));
nsCOMPtr<nsIAccessNode> captionAccessNode = do_QueryInterface(captionAccessible);
if (captionAccessNode) {
nsCOMPtr<nsIDOMNode> captionNode;
captionAccessNode->GetDOMNode(getter_AddRefs(captionNode));
nsCOMPtr<nsIContent> captionContent = do_QueryInterface(captionNode);
if (captionContent)
// Use summary as description if it weren't used as a name.
// XXX: get rid code duplication with NameInternal().
nsAccessible* caption = Caption();
if (caption) {
nsIContent* captionContent = caption->GetContent();
if (captionContent) {
nsAutoString captionText;
nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent,
&aDescription);
&captionText);
if (!captionText.IsEmpty()) { // summary isn't used as a name.
mContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::summary,
aDescription);
}
}
}
#ifdef SHOW_LAYOUT_HEURISTIC
if (aDescription.IsEmpty()) {
PRBool isProbablyForLayout;

View File

@ -141,6 +141,14 @@ public:
virtual PRUint64 NativeState();
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
// TableAccessible
inline nsAccessible* Caption() const
{
nsAccessible* child = mChildren.SafeElementAt(0, nsnull);
return child && child->Role() == nsIAccessibleRole::ROLE_CAPTION ?
child : nsnull;
}
// nsHTMLTableAccessible
/**

View File

@ -7,7 +7,6 @@ var gRuleDoc = null;
// Debuggin stuff.
var gDumpToConsole = false;
gA11yEventDumpToConsole = gDumpToConsole;
/**
* Start name tests. Run through markup elements and test names for test
@ -149,7 +148,7 @@ function testNameForRule(aElm, aRuleElm)
testNameForAttrRule(aElm, aRuleElm);
} else if (aRuleElm.hasAttribute("elm") && aRuleElm.hasAttribute("elmattr")) {
} else if (aRuleElm.hasAttribute("elm")) {
if (gDumpToConsole) {
dump("\nProcessing rule { elm: " + aRuleElm.getAttribute("elm") +
", elmattr: " + aRuleElm.getAttribute("elmattr") +" }\n");
@ -178,7 +177,7 @@ function testNameForAttrRule(aElm, aRule)
if (type == "string") {
name = attrValue;
} else if (type == "ref") {
} else if (type == "ref" && attrValue) {
var ids = attrValue.split(/\s+/);
for (var idx = 0; idx < ids.length; idx++) {
var labelElm = getNode(ids[idx]);
@ -197,37 +196,53 @@ function testNameForAttrRule(aElm, aRule)
}
function testNameForElmRule(aElm, aRule)
{
var elm = aRule.getAttribute("elm");
var elmattr = aRule.getAttribute("elmattr");
{
var labelElm;
var filter = {
acceptNode: function filter_acceptNode(aNode)
{
if (aNode.localName == this.mLocalName &&
aNode.getAttribute(this.mAttrName) == this.mAttrValue)
return NodeFilter.FILTER_ACCEPT;
var tagname = aRule.getAttribute("elm");
var attrname = aRule.getAttribute("elmattr");
if (attrname) {
var filter = {
acceptNode: function filter_acceptNode(aNode)
{
if (aNode.localName == this.mLocalName &&
aNode.getAttribute(this.mAttrName) == this.mAttrValue)
return NodeFilter.FILTER_ACCEPT;
return NodeFilter.FILTER_SKIP;
},
return NodeFilter.FILTER_SKIP;
},
mLocalName: elm,
mAttrName: elmattr,
mAttrValue: aElm.getAttribute("id")
};
mLocalName: tagname,
mAttrName: attrname,
mAttrValue: aElm.getAttribute("id")
};
var treeWalker = document.createTreeWalker(document.body,
NodeFilter.SHOW_ELEMENT,
filter, false);
var labelElm = treeWalker.nextNode();
var msg = "Element '" + elm + "' test.";
var treeWalker = document.createTreeWalker(document.body,
NodeFilter.SHOW_ELEMENT,
filter, false);
labelElm = treeWalker.nextNode();
} else {
// if attrname is empty then look for the element in subtree.
labelElm = aElm.getElementsByTagName(tagname)[0];
if (!labelElm)
labelElm = aElm.getElementsByTagName("html:" + tagname)[0];
}
if (!labelElm) {
ok(false, msg + " Failed to find '" + tagname + "' element.");
gTestIterator.iterateNext();
return;
}
var msg = "Element '" + tagname + "' test.";
testName(aElm, labelElm.getAttribute("a11yname"), msg);
var parentNode = labelElm.parentNode;
if (gDumpToConsole) {
dump("\nProcessed elm rule. Wait for reorder event on " +
prettyName(parentNode) + "'\n");
prettyName(parentNode) + "\n");
}
waitForEvent(EVENT_REORDER, parentNode,
gTestIterator.iterateNext, gTestIterator);

View File

@ -1,31 +1,50 @@
<?xml version="1.0"?>
<!--
This XML file is used to create sequence of accessible name tests. It consist of
two sections. The first section 'ruledfn' declares name computation rules.
The second section 'rulesample' defines markup samples we need to check name
computation rules for.
This XML file is used to create sequence of accessible name tests. It consist
of two sections. The first section 'ruledfn' declares name computation rules.
The second section 'rulesample' defines markup samples we need to check name
computation rules for.
Section 'ruledfn' consist of 'ruleset' elements. Every 'ruleset' element is
<ruledfn>
<ruleset>
<rule>
Section 'ruledfn' contains 'ruleset' elements. Every 'ruleset' element is
presented by 'rule' elements so that sequence of 'rule' elements gives the
sequence of name computations rules. Every 'rule' element can be one of four
types.
* name is equal to the value of attribute presented on the element. Example,
'aria-label' attribute. In this case 'rule' element has 'attr' attribute
pointing to attribute name and 'type' attribute with 'string' value. For
example, <rule attr="aria-label" type="string"/>.
* name is calculated from elements that are pointed to by attribute value on
the element. Example is 'aria-labelledby'. In this case 'rule' element
has 'attr' attribute holding the sequence of IDs of elements used to
compute the name, in addition the 'rule' element has 'type' attribute with
'ref' value. For example, <rule attr="aria-labelledby" type="ref"/>.
* name is calculated from another element. Example, html:label@for element.
In this case 'rule' element has 'elm' and 'elmattr' attributes. These
attributes are used to find an element by tagname and attribute with value
equaled to ID of the element. For example, <rule elm="label" elmattr="for"/>.
* name is computed from subtree. Example, html:button. In this case 'rule'
element has 'fromsubtree' attribute with 'true' value. For example,
<rule fromsubtree="true"/>
* <rule attr='' type='string'/> used when name is equal to the value of
attribute presented on the element.
Example, 'aria-label' attribute. In this case 'rule' element has 'attr'
attribute pointing to attribute name and 'type' attribute with 'string'
value. For example, <rule attr="aria-label" type="string"/>.
* <rule attr='' type='ref'/> used when name is calculated from elements that
are pointed to by attribute value on the element.
Example is 'aria-labelledby'. In this case 'rule' element has 'attr'
attribute holding the sequence of IDs of elements used to compute the name,
in addition the 'rule' element has 'type' attribute with 'ref' value.
For example, <rule attr="aria-labelledby" type="ref"/>.
* <rule elm='' elmattr=''/> used when name is calculated from another
element. These attributes are used to find an element by tagname and
attribute with value equaled to ID of the element. If 'elmattr' is missed
then element from subtree with the given tagname is used.
Example, html:label@for element, <rule elm="label" elmattr="for"/>.
Example, html:caption element, <rule elm="caption"/>
* <rule fromsubtree='true'/> used when name is computed from subtree.
Example, html:button. In this case 'rule' element has 'fromsubtree'
attribute with 'true' value.
<rulesample>
<markup ruleset=''>
Section 'rulesample' provides set of markup samples ('markup' elements). Every
'markup' element contains an element that accessible name will be computed for
@ -120,6 +139,12 @@
<rule attr="title" type="string"/>
</ruleset>
<ruleset id="htmltable">
<ruleset ref="htmlelm_start"/>
<rule elm="caption"/>
<rule attr="summary" type="string"/>
<ruleset ref="htmlelm_end"/>
</ruleset>
</ruledfn>
<rulesample>
@ -210,6 +235,25 @@
</html:table>
</markup>
<markup ref="html:table" ruleset="htmltable"
id="markup6test">
<html:span id="l1" a11yname="lby_tst6_1">lby_tst6_1</html:span>
<html:span id="l2" a11yname="lby_tst6_2">lby_tst6_2</html:span>
<html:label for="t" a11yname="label_tst6">label_tst6</html:label>
<!-- layout frame are recreated due to varous reasons, here's text frame
placed after caption frame triggres table frame recreation when
caption element is removed from DOM; get rid text node after caption
node to make the test working -->
<html:table id="t" aria-label="arialabel_tst6"
aria-labelledby="l1 l2"
summary="summary_tst6"
title="title_tst6">
<html:caption a11yname="caption_tst6">caption_tst6</html:caption><html:tr>
<html:td>cell1</html:td>
<html:td>cell2</html:td>
</html:tr>
</html:table>
</markup>
</rulesample>
</rules>

View File

@ -21,6 +21,8 @@
<script type="application/javascript">
// gA11yEventDumpID = "eventdump";
//gDumpToConsole = true;
//gA11yEventDumpToConsole = true;
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(testNames);
@ -35,6 +37,12 @@
title="nsIAccessible::name calculation for elements">
Mozilla Bug 459635
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=666212"
title="summary attribute content mapped to accessible name in MSAA">
Mozilla Bug 666212
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">

View File

@ -38,6 +38,15 @@
// Description from content of h2.
testDescr("p", "heading");
// From table summary (caption is used as a name)
testDescr("table1", "summary");
// Empty (summary is used as a name)
testDescr("table2", "");
// From title (summary is used as a name)
testDescr("table3", "title");
SimpleTest.finish();
}
@ -54,6 +63,11 @@
title="@title attribute no longer exposed on accDescription">
Mozilla Bug 489944
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=666212"
title="summary attribute content mapped to accessible name in MSAA">
Mozilla Bug 666212
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@ -66,5 +80,18 @@
<h2 id="heading">heading</h2>
<p id="p" aria-describedby="heading" role="button">click me</p>
<table id="table1" summary="summary">
<caption>caption</caption>
<tr><td>cell</td></tr>
</table>
<table id="table2" summary="summary">
<tr><td>cell</td></tr>
</table>
<table id="table3" summary="summary" title="title">
<tr><td>cell</td></tr>
</table>
</body>
</html>