Bug 1572677: Correct a11y exposure for HTML elements with associated XUL labels. r=MarcoZ

A XUL <label control="id"> can refer to an HTML element.
Keyboard and mouse support for this already works, but previously, this wasn't being exposed via accessibility APIs.

1. Split the code to get the name from an associated XUL label out of Accessible::XULElmName into a new function Accessible::NameFromAssociatedXULLabel.
2. Use NameFromAssociatedXULLabel for HTMl elements.
3. Update AccessKey and RelationByType to support HTML elements with associated XUL labels.
4. Rename accessible/tests/mochitest/actions/test_keys_menu.xhtml to test_keys.xhtml so it can cover accessKey outside of menus.
5. Add tests.

Differential Revision: https://phabricator.services.mozilla.com/D55057

--HG--
rename : accessible/tests/mochitest/actions/test_keys_menu.xhtml => accessible/tests/mochitest/actions/test_keys.xhtml
extra : moz-landing-system : lando
This commit is contained in:
James Teh 2019-11-28 06:36:04 +00:00
parent 42c2ecdc42
commit 8d5f485ca6
6 changed files with 57 additions and 19 deletions

View File

@ -230,8 +230,8 @@ KeyBinding Accessible::AccessKey() const {
HTMLLabelIterator iter(Document(), this,
HTMLLabelIterator::eSkipAncestorLabel);
label = iter.Next();
} else if (mContent->IsXULElement()) {
}
if (!label) {
XULLabelIterator iter(Document(), mContent);
label = iter.Next();
}
@ -753,6 +753,21 @@ void Accessible::TakeFocus() const {
}
}
void Accessible::NameFromAssociatedXULLabel(DocAccessible* aDocument,
nsIContent* aElm, nsString& aName) {
Accessible* label = nullptr;
XULLabelIterator iter(aDocument, aElm);
while ((label = iter.Next())) {
// Check if label's value attribute is used
label->Elm()->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName);
if (aName.IsEmpty()) {
// If no value attribute, a non-empty label must contain
// children that define its text -- possibly using HTML
nsTextEquivUtils::AppendTextEquivFromContent(label, label->Elm(), &aName);
}
}
}
void Accessible::XULElmName(DocAccessible* aDocument, nsIContent* aElm,
nsString& aName) {
/**
@ -786,18 +801,7 @@ void Accessible::XULElmName(DocAccessible* aDocument, nsIContent* aElm,
// CASES #2 and #3 ------ label as a child or <label control="id" ... >
// </label>
if (aName.IsEmpty()) {
Accessible* label = nullptr;
XULLabelIterator iter(aDocument, aElm);
while ((label = iter.Next())) {
// Check if label's value attribute is used
label->Elm()->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aName);
if (aName.IsEmpty()) {
// If no value attribute, a non-empty label must contain
// children that define its text -- possibly using HTML
nsTextEquivUtils::AppendTextEquivFromContent(label, label->Elm(),
&aName);
}
}
NameFromAssociatedXULLabel(aDocument, aElm, aName);
}
aName.CompressWhitespace();
@ -1620,9 +1624,8 @@ Relation Accessible::RelationByType(RelationType aType) const {
new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_labelledby));
if (mContent->IsHTMLElement()) {
rel.AppendIter(new HTMLLabelIterator(Document(), this));
} else if (mContent->IsXULElement()) {
rel.AppendIter(new XULLabelIterator(Document(), mContent));
}
rel.AppendIter(new XULLabelIterator(Document(), mContent));
return rel;
}
@ -1979,6 +1982,11 @@ ENameValueFlag Accessible::NativeName(nsString& aName) const {
if (!aName.IsEmpty()) return eNameOK;
NameFromAssociatedXULLabel(mDoc, mContent, aName);
if (!aName.IsEmpty()) {
return eNameOK;
}
nsTextEquivUtils::GetNameFromSubtree(this, aName);
return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
}

View File

@ -1075,6 +1075,13 @@ class Accessible : public nsISupports {
*/
void ARIAName(nsString& aName) const;
/**
* Returns the accessible name specified for this control using XUL
* <label control="id" ...>.
*/
static void NameFromAssociatedXULLabel(DocAccessible* aDocument,
nsIContent* aElm, nsString& aName);
/**
* Return the name for XUL element.
*/

View File

@ -9,7 +9,7 @@ support-files =
[test_general.html]
[test_general.xhtml]
[test_keys.html]
[test_keys_menu.xhtml]
[test_keys.xhtml]
[test_link.html]
[test_media.html]
[test_select.html]

View File

@ -4,6 +4,7 @@
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="Accessible XUL access keys and shortcut keys tests">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
@ -17,8 +18,8 @@
<![CDATA[
function openMenu(aMenuID, aMenuitemID)
{
this.menuNode = getNode(aMenuID),
this.menuitemNode = getNode(aMenuitemID),
this.menuNode = getNode(aMenuID);
this.menuitemNode = getNode(aMenuitemID);
this.eventSeq = [
new invokerChecker(EVENT_FOCUS, this.menuNode)
@ -53,6 +54,11 @@
var gQueue = null;
function doTest()
{
// HTML element should get accessKey from associated XUL label.
let input = getAccessible("input");
is(input.accessKey, (MAC ? "⌃⌥i" : "Alt+Shift+i"),
"Wrong accessKey on input");
gQueue = new eventQueue();
gQueue.push(new openMenu("menu", "menuitem"));
gQueue.invoke(); // Will call SimpleTest.finish();
@ -78,6 +84,9 @@
</body>
<vbox flex="1">
<label control="input" accesskey="i">input</label>
<html:input id="input"/>
<keyset>
<key key="l" modifiers="control" id="key1"/>
</keyset>

View File

@ -100,6 +100,9 @@
// Multiple labels for single button: XUL button takes the last one.
testName("btn_label_4", "label5");
// Label associated with HTML element.
testName("input_label", "input label");
//////////////////////////////////////////////////////////////////////////
// tooltiptext (if nothing above isn't presented then tooltiptext is used)
@ -267,6 +270,9 @@
<label control="btn_label_4">label4</label>
<label control="btn_label_4">label5</label>
<button id="btn_label_4"/>
<label control="input_label">input label</label>
<html:input id="input_label"/>
</hbox>
<!-- tooltiptext -->

View File

@ -4,6 +4,7 @@
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="nsIAccessible::getAccessibleRelated() tests">
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
@ -38,6 +39,10 @@
testRelation("label4", RELATION_LABEL_FOR, "checkbox3");
testRelation("checkbox3", RELATION_LABELLED_BY, ["label3", "label4"]);
// xul:label@control referring to HTML element
testRelation("label_input", RELATION_LABEL_FOR, "input");
testRelation("input", RELATION_LABELLED_BY, "label_input");
// aria-describedby
testRelation("descr1", RELATION_DESCRIPTION_FOR, "checkbox4");
testRelation("checkbox4", RELATION_DESCRIBED_BY, "descr1");
@ -158,6 +163,9 @@
<description role="checkbox" id="checkbox3"
aria-labelledby="label3 label4"/>
<label id="label_input" control="input">label</label>
<html:input id="input"/>
<description id="descr1">description</description>
<description role="checkbox" id="checkbox4" aria-describedby="descr1"/>