зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1578494 - separate accessible roles into the ones that are expected to be interactive from keyboard accessible ones, better account for ARIA comboboxes and listboxes. r=nchevobbe
Differential Revision: https://phabricator.services.mozilla.com/D45215 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
fa78314c8f
Коммит
9e5e47e8ff
|
@ -54,32 +54,39 @@ const SHOW_LONG_DESC_ACTION = "showlongdesc";
|
|||
const FOCUS_PSEUDO_CLASS = ":focus";
|
||||
const MOZ_FOCUSRING_PSEUDO_CLASS = ":-moz-focusring";
|
||||
|
||||
const INTERACTIVE_ROLES = new Set([
|
||||
const KEYBOARD_FOCUSABLE_ROLES = new Set([
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONMENU,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_RICH_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_COMBOBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_COMBOBOX_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_EDITCOMBOBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_ENTRY,
|
||||
Ci.nsIAccessibleRole.ROLE_LINK,
|
||||
Ci.nsIAccessibleRole.ROLE_LISTBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_MENUITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_PAGETAB,
|
||||
Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
|
||||
Ci.nsIAccessibleRole.ROLE_PARENT_MENUITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_RICH_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_SLIDER,
|
||||
Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_SUMMARY,
|
||||
Ci.nsIAccessibleRole.ROLE_SWITCH,
|
||||
Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
|
||||
]);
|
||||
|
||||
const INTERACTIVE_ROLES = new Set([
|
||||
...KEYBOARD_FOCUSABLE_ROLES,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_RICH_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_COMBOBOX_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_MENUITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_OPTION,
|
||||
Ci.nsIAccessibleRole.ROLE_OUTLINE,
|
||||
Ci.nsIAccessibleRole.ROLE_OUTLINEITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_PAGETAB,
|
||||
Ci.nsIAccessibleRole.ROLE_PARENT_MENUITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_RICH_OPTION,
|
||||
]);
|
||||
|
||||
/**
|
||||
* Determine if a node is dead or is not an element node.
|
||||
*
|
||||
|
@ -295,7 +302,7 @@ function interactiveRule(accessible) {
|
|||
* when enabled, audit report object otherwise.
|
||||
*/
|
||||
function focusableRule(accessible) {
|
||||
if (!INTERACTIVE_ROLES.has(accessible.role)) {
|
||||
if (!KEYBOARD_FOCUSABLE_ROLES.has(accessible.role)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -315,6 +322,21 @@ function focusableRule(accessible) {
|
|||
return null;
|
||||
}
|
||||
|
||||
let ariaRoles;
|
||||
try {
|
||||
ariaRoles = accessible.attributes.getStringProperty("xml-roles");
|
||||
} catch (e) {
|
||||
// No xml-roles. nsPersistentProperties throws if the attribute for a key
|
||||
// is not found.
|
||||
}
|
||||
if (
|
||||
ariaRoles &&
|
||||
(ariaRoles.includes("combobox") || ariaRoles.includes("listbox"))
|
||||
) {
|
||||
// Do not force ARIA combobox or listbox to be focusable.
|
||||
return null;
|
||||
}
|
||||
|
||||
return { score: FAIL, issue: INTERACTIVE_NOT_FOCUSABLE };
|
||||
}
|
||||
|
||||
|
@ -331,7 +353,11 @@ function focusableRule(accessible) {
|
|||
* interactive role, audit report object otherwise.
|
||||
*/
|
||||
function semanticsRule(accessible) {
|
||||
if (INTERACTIVE_ROLES.has(accessible.role)) {
|
||||
if (
|
||||
INTERACTIVE_ROLES.has(accessible.role) ||
|
||||
// Visible listboxes will have focusable state when inside comboboxes.
|
||||
accessible.role === Ci.nsIAccessibleRole.ROLE_COMBOBOX_LIST
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -129,6 +129,41 @@ add_task(async function() {
|
|||
"#img-4",
|
||||
{ score: FAIL, issue: MOUSE_INTERACTIVE_ONLY },
|
||||
],
|
||||
["Focusable button with aria-haspopup.", "#buttonmenu-1", null],
|
||||
[
|
||||
"Not focusable aria button with aria-haspopup.",
|
||||
"#buttonmenu-2",
|
||||
{
|
||||
score: FAIL,
|
||||
issue: INTERACTIVE_NOT_FOCUSABLE,
|
||||
},
|
||||
],
|
||||
["Focusable checkbox.", "#checkbox-1", null],
|
||||
["Focusable select element size > 1", "#listbox-1", null],
|
||||
["Focusable select element with one option", "#combobox-1", null],
|
||||
["Focusable select element with no options", "#combobox-2", null],
|
||||
["Focusable select element with two options", "#combobox-3", null],
|
||||
[
|
||||
"Non-focusable aria combobox with one aria option.",
|
||||
"#editcombobox-1",
|
||||
null,
|
||||
],
|
||||
["Non-focusable aria combobox with no options.", "#editcombobox-2", null],
|
||||
["Focusable aria combobox with no options.", "#editcombobox-3", null],
|
||||
[
|
||||
"Non-focusable aria switch",
|
||||
"#switch-1",
|
||||
{
|
||||
score: FAIL,
|
||||
issue: INTERACTIVE_NOT_FOCUSABLE,
|
||||
},
|
||||
],
|
||||
["Focusable aria switch", "#switch-2", null],
|
||||
[
|
||||
"Combobox list that is visible (has focusable state)",
|
||||
"#owned_listbox",
|
||||
null,
|
||||
],
|
||||
];
|
||||
|
||||
for (const [description, selector, expected] of tests) {
|
||||
|
@ -144,16 +179,27 @@ add_task(async function() {
|
|||
}
|
||||
|
||||
info("Text leaf inside a link (jump action is propagated to the text link)");
|
||||
const node = await walker.querySelector(walker.rootNode, "#link-5");
|
||||
const parent = await a11yWalker.getAccessibleFor(node);
|
||||
const front = (await parent.children())[0];
|
||||
const audit = await front.audit({ types: [KEYBOARD] });
|
||||
let node = await walker.querySelector(walker.rootNode, "#link-5");
|
||||
let parent = await a11yWalker.getAccessibleFor(node);
|
||||
let front = (await parent.children())[0];
|
||||
let audit = await front.audit({ types: [KEYBOARD] });
|
||||
Assert.deepEqual(
|
||||
audit[KEYBOARD],
|
||||
null,
|
||||
"Text leafs are excluded from semantics rule."
|
||||
);
|
||||
|
||||
info("Combobox list that is invisible");
|
||||
node = await walker.querySelector(walker.rootNode, "#combobox-1");
|
||||
parent = await a11yWalker.getAccessibleFor(node);
|
||||
front = (await parent.children())[0];
|
||||
audit = await front.audit({ types: [KEYBOARD] });
|
||||
Assert.deepEqual(
|
||||
audit[KEYBOARD],
|
||||
null,
|
||||
"Combobox lists (invisible) are excluded from semantics rule."
|
||||
);
|
||||
|
||||
await accessibility.disable();
|
||||
await waitForA11yShutdown();
|
||||
await target.destroy();
|
||||
|
|
|
@ -40,5 +40,30 @@
|
|||
<img id="img-2" longdesc="https://example.com" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAAJklEQVRIie3NMREAAAgAoe9fWls4eAzMVM0xoVAoFAqFQqFQ+C9chp4NHvu+4Q4AAAAASUVORK5CYII=" alt="alt text">
|
||||
<img id="img-3" longdesc="https://example.com" onclick="console.log('foo');" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAAJklEQVRIie3NMREAAAgAoe9fWls4eAzMVM0xoVAoFAqFQqFQ+C9chp4NHvu+4Q4AAAAASUVORK5CYII=" alt="alt text">
|
||||
<img id="img-4" onclick="console.log('foo');" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAAJklEQVRIie3NMREAAAgAoe9fWls4eAzMVM0xoVAoFAqFQqFQ+C9chp4NHvu+4Q4AAAAASUVORK5CYII=" alt="alt text">
|
||||
<button id="buttonmenu-1" aria-haspopup="true">I have a popup</button>
|
||||
<div role="button" id="buttonmenu-2" aria-haspopup="true">I have a popup</div>
|
||||
<input id="checkbox-1" type="checkbox" name="hello" />
|
||||
<select id="listbox-1" size="2">
|
||||
<option id="lb_orange">orange</option>
|
||||
<option id="lb_apple">apple</option>
|
||||
</select>
|
||||
<select id="combobox-1"></select>
|
||||
<select id="combobox-2"><option>One</option></select>
|
||||
<select id="combobox-3">
|
||||
<option id="cb_orange">orange</option>
|
||||
<option id="cb_apple">apple</option>
|
||||
</select>
|
||||
<div id="editcombobox-1" role="combobox"><span role="option">One</span></div>
|
||||
<span id="editcombobox-2"role="combobox"></span>
|
||||
<span id="editcombobox-3"role="combobox" tabindex="0"></span>
|
||||
<span id="switch-1" role="switch"></span>
|
||||
<span id="switch-2" role="switch" tabindex="0"></span>
|
||||
<div aria-label="Tag" role="combobox" aria-expanded="true" aria-owns="owned_listbox" aria-haspopup="listbox">
|
||||
<input type="text" aria-autocomplete="list" aria-controls="owned_listbox" aria-activedescendant="selected_option">
|
||||
</div>
|
||||
<ul role="listbox" id="owned_listbox">
|
||||
<li role="option">Zebra</li>
|
||||
<li role="option" id="selected_option">Zoom</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Загрузка…
Ссылка в новой задаче