mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
Bug 1577381: Correct accessibility exposure for optgroups in content select dropdowns. r=eeejay,NeilDeakin
For remote content documents, select dropdowns (for <select size="1">) are rendered in the parent process using a XUL menupopup. This means that the accessibility code for HTML selects doesn't apply. In the menupopup, the optgroup is a sibling of its contained options. For accessibility, we want to preserve the hierarchy such that the options are inside the optgroup. We do this using aria-owns on the optgroup item. This required some tweaks to XULMenuitemAccessible, as it couldn't previously handle grouping Accessibles between the menupopup and its items. Differential Revision: https://phabricator.services.mozilla.com/D43901 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
964bceeb91
commit
1adf6ac726
@ -20,6 +20,8 @@ support-files =
|
||||
[browser_caching_states.js]
|
||||
[browser_caching_value.js]
|
||||
|
||||
[browser_contentSelectDropdown.js]
|
||||
|
||||
# Events tests
|
||||
[browser_events_announcement.js]
|
||||
skip-if = e10s && os == 'win' # Bug 1288839
|
||||
|
@ -0,0 +1,65 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from ../../mochitest/role.js */
|
||||
loadScripts({ name: "role.js", dir: MOCHITESTS_DIR });
|
||||
|
||||
const snippet = `
|
||||
<select id="select">
|
||||
<option>o1</option>
|
||||
<optgroup label="g1">
|
||||
<option>g1o1</option>
|
||||
<option>g1o2</option>
|
||||
</optgroup>
|
||||
<optgroup label="g2">
|
||||
<option>g2o1</option>
|
||||
<option>g2o2</option>
|
||||
</optgroup>
|
||||
<option>o2</option>
|
||||
</select>
|
||||
`;
|
||||
|
||||
addAccessibleTask(snippet, async function(browser, accDoc) {
|
||||
await invokeFocus(browser, "select");
|
||||
// Expand the select. A dropdown item should get focus.
|
||||
// Note that the dropdown is rendered in the parent process.
|
||||
let focused = waitForEvent(
|
||||
EVENT_FOCUS,
|
||||
event => event.accessible.role == ROLE_COMBOBOX_OPTION,
|
||||
"Dropdown item focused after select expanded"
|
||||
);
|
||||
await BrowserTestUtils.synthesizeKey(
|
||||
"KEY_ArrowDown",
|
||||
{ altKey: true },
|
||||
browser
|
||||
);
|
||||
let event = await focused;
|
||||
let dropdown = event.accessible.parent;
|
||||
|
||||
let selectedOptionChildren = [];
|
||||
if (MAC) {
|
||||
// Checkmark is part of the Mac menu styling.
|
||||
selectedOptionChildren = [{ STATICTEXT: [] }];
|
||||
}
|
||||
let tree = {
|
||||
COMBOBOX_LIST: [
|
||||
{ COMBOBOX_OPTION: selectedOptionChildren },
|
||||
{ GROUPING: [{ COMBOBOX_OPTION: [] }, { COMBOBOX_OPTION: [] }] },
|
||||
{ GROUPING: [{ COMBOBOX_OPTION: [] }, { COMBOBOX_OPTION: [] }] },
|
||||
{ COMBOBOX_OPTION: [] },
|
||||
],
|
||||
};
|
||||
testAccessibleTree(dropdown, tree);
|
||||
|
||||
// Collapse the select. Focus should return to the select.
|
||||
focused = waitForEvent(
|
||||
EVENT_FOCUS,
|
||||
"select",
|
||||
"select focused after collapsed"
|
||||
);
|
||||
await BrowserTestUtils.synthesizeKey("KEY_Escape", {}, browser);
|
||||
await focused;
|
||||
});
|
@ -221,8 +221,10 @@ role XULMenuitemAccessible::NativeRole() const {
|
||||
nsCOMPtr<nsIDOMXULContainerElement> xulContainer = Elm()->AsXULContainer();
|
||||
if (xulContainer) return roles::PARENT_MENUITEM;
|
||||
|
||||
if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
|
||||
Accessible* widget = ContainerWidget();
|
||||
if (widget && widget->Role() == roles::COMBOBOX_LIST) {
|
||||
return roles::COMBOBOX_OPTION;
|
||||
}
|
||||
|
||||
if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
|
||||
nsGkAtoms::radio, eCaseMatters))
|
||||
@ -287,11 +289,19 @@ Accessible* XULMenuitemAccessible::ContainerWidget() const {
|
||||
if (menuFrame) {
|
||||
nsMenuParent* menuParent = menuFrame->GetMenuParent();
|
||||
if (menuParent) {
|
||||
if (menuParent->IsMenuBar()) // menubar menu
|
||||
return mParent;
|
||||
|
||||
// a menupoup or parent menu item
|
||||
if (menuParent->IsMenu()) return mParent;
|
||||
nsBoxFrame* frame = nullptr;
|
||||
if (menuParent->IsMenuBar()) { // menubar menu
|
||||
frame = static_cast<nsMenuBarFrame*>(menuParent);
|
||||
} else if (menuParent->IsMenu()) { // a menupopup or parent menu item
|
||||
frame = static_cast<nsMenuPopupFrame*>(menuParent);
|
||||
}
|
||||
if (frame) {
|
||||
nsIContent* content = frame->GetContent();
|
||||
if (content) {
|
||||
MOZ_ASSERT(mDoc);
|
||||
return mDoc->GetAccessible(content);
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise it's different kind of popups (like panel or tooltip), it
|
||||
// shouldn't be a real case.
|
||||
|
@ -408,11 +408,15 @@ var SelectParentHelper = {
|
||||
) {
|
||||
let element = menulist.menupopup;
|
||||
|
||||
let ariaOwns = "";
|
||||
for (let option of options) {
|
||||
let isOptGroup = option.tagName == "OPTGROUP";
|
||||
let item = element.ownerDocument.createXULElement(
|
||||
isOptGroup ? "menucaption" : "menuitem"
|
||||
);
|
||||
if (isOptGroup) {
|
||||
item.setAttribute("role", "group");
|
||||
}
|
||||
let style = uniqueOptionStyles[option.styleIndex];
|
||||
|
||||
item.setAttribute("label", option.textContent);
|
||||
@ -487,6 +491,16 @@ var SelectParentHelper = {
|
||||
item.removeAttribute("customoptionstyling");
|
||||
}
|
||||
|
||||
if (parentElement) {
|
||||
// In the menupopup, the optgroup is a sibling of its contained options.
|
||||
// For accessibility, we want to preserve the hierarchy such that the
|
||||
// options are inside the optgroup. We do this using aria-owns on the
|
||||
// parent.
|
||||
item.id = "ContentSelectDropdownOption" + nthChildIndex;
|
||||
item.setAttribute("aria-level", "2");
|
||||
ariaOwns += item.id + " ";
|
||||
}
|
||||
|
||||
element.appendChild(item);
|
||||
nthChildIndex++;
|
||||
|
||||
@ -536,6 +550,10 @@ var SelectParentHelper = {
|
||||
}
|
||||
}
|
||||
|
||||
if (parentElement && ariaOwns) {
|
||||
parentElement.setAttribute("aria-owns", ariaOwns);
|
||||
}
|
||||
|
||||
// Check if search pref is enabled, if this is the first time iterating through
|
||||
// the dropdown, and if the list is long enough for a search element to be added.
|
||||
if (
|
||||
|
Loading…
Reference in New Issue
Block a user