mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-08 12:22:34 +00:00
Bug 1681292: Ensure menu items correctly expose AXVisibleChildren and AXChildren r=eeejay
Differential Revision: https://phabricator.services.mozilla.com/D99949
This commit is contained in:
parent
29a8164b84
commit
2bc463ad54
@ -217,6 +217,9 @@
|
||||
// AXIdentifier
|
||||
- (NSString* _Nullable)moxIdentifier;
|
||||
|
||||
// AXVisibleChildren
|
||||
- (NSArray* _Nullable)moxVisibleChildren;
|
||||
|
||||
// Outline Attributes
|
||||
|
||||
// AXDisclosing
|
||||
|
@ -86,6 +86,15 @@
|
||||
// override
|
||||
- (NSString*)moxLabel;
|
||||
|
||||
// override
|
||||
- (NSArray*)moxChildren;
|
||||
|
||||
// override
|
||||
- (NSArray*)moxVisibleChildren;
|
||||
|
||||
// override
|
||||
- (id)moxTitleUIElement;
|
||||
|
||||
// override
|
||||
- (void)moxPostNotification:(NSString*)notification;
|
||||
|
||||
|
@ -154,6 +154,48 @@ using namespace mozilla::a11y;
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (NSArray*)moxChildren {
|
||||
// We differ from Webkit and Apple-native menus here; they expose
|
||||
// all children regardless of whether or not the menu is open.
|
||||
// In testing, VoiceOver doesn't seem to actually care what happens
|
||||
// here as long as AXVisibleChildren is exposed correctly, so
|
||||
// we expose children only when the menu is open to avoid
|
||||
// changing ignoreWithParent/ignoreChild and/or isAccessibilityElement
|
||||
if (mIsOpened) {
|
||||
return [super moxChildren];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSArray*)moxVisibleChildren {
|
||||
// VO expects us to expose two lists of children on menus: all children
|
||||
// (done above in moxChildren), and children which are visible (here).
|
||||
// In our code, these are essentially the same list, since at the time of
|
||||
// wiritng we filter for visibility in isAccessibilityElement before
|
||||
// passing anything to VO.
|
||||
return [[self moxChildren]
|
||||
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
|
||||
mozAccessible* child,
|
||||
NSDictionary* bindings) {
|
||||
if (Accessible* acc = [child geckoAccessible].AsAccessible()) {
|
||||
if (acc->IsContent() && acc->GetContent()->IsXULElement()) {
|
||||
return ((acc->VisibilityState() & states::INVISIBLE) == 0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}]];
|
||||
}
|
||||
|
||||
- (id)moxTitleUIElement {
|
||||
id parent = [self moxUnignoredParent];
|
||||
if ([parent isKindOfClass:[mozAccessible class]] &&
|
||||
[parent geckoAccessible].Role() == roles::PARENT_MENUITEM) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)moxPostNotification:(NSString*)notification {
|
||||
[super moxPostNotification:notification];
|
||||
|
||||
|
@ -179,3 +179,99 @@ add_task(async () => {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test context menu
|
||||
*/
|
||||
add_task(async () => {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
{
|
||||
gBrowser,
|
||||
url:
|
||||
'data:text/html,<a id="exampleLink" href="https://example.com">link</a>',
|
||||
},
|
||||
async browser => {
|
||||
// synthesize a right click on the link to open the link context menu
|
||||
let menu = document.getElementById("contentAreaContextMenu");
|
||||
await BrowserTestUtils.synthesizeMouse(
|
||||
"#exampleLink",
|
||||
2,
|
||||
2,
|
||||
{ type: "contextmenu" },
|
||||
browser
|
||||
);
|
||||
await BrowserTestUtils.waitForPopupEvent(menu, "shown");
|
||||
menu = await getMacAccessible(menu);
|
||||
const menuChildren = menu.getAttributeValue("AXChildren");
|
||||
// menu contains 11 items and 3 splitters for 14 items total
|
||||
is(
|
||||
menuChildren.length,
|
||||
14,
|
||||
"Context menu on link contains fourteen items"
|
||||
);
|
||||
|
||||
for (let i = 0; i < menuChildren.length; i++) {
|
||||
// items at indicies 4, 9, and 11 are the splitters, everything else should be a menu item
|
||||
if (i == 4 || i == 9 || i == 11) {
|
||||
is(
|
||||
menuChildren[i].getAttributeValue("AXRole"),
|
||||
"AXSplitter",
|
||||
"found splitter in menu"
|
||||
);
|
||||
} else {
|
||||
is(
|
||||
menuChildren[i].getAttributeValue("AXRole"),
|
||||
"AXMenuItem",
|
||||
"found menu item in menu"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// submenus are at indicies 1 and 10
|
||||
// first check they have no children when hidden
|
||||
is(
|
||||
menuChildren[1].getAttributeValue("AXChildren").length,
|
||||
0,
|
||||
"Submenu 1 has no chldren when hidden"
|
||||
);
|
||||
is(
|
||||
menuChildren[10].getAttributeValue("AXChildren").length,
|
||||
0,
|
||||
"Submenu 2 has no chldren when hidden"
|
||||
);
|
||||
|
||||
// focus the first submenu
|
||||
const contextMenu = document.getElementById(
|
||||
"context-openlinkinusercontext-menu"
|
||||
);
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown");
|
||||
EventUtils.synthesizeKey("KEY_ArrowRight");
|
||||
await BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
|
||||
|
||||
// verify submenu-menuitem's attributes
|
||||
is(
|
||||
menuChildren[1].getAttributeValue("AXChildren").length,
|
||||
1,
|
||||
"Submenu 1 has one child when open"
|
||||
);
|
||||
const subMenu = menuChildren[1].getAttributeValue("AXChildren")[0];
|
||||
is(
|
||||
subMenu.getAttributeValue("AXRole"),
|
||||
"AXMenu",
|
||||
"submenu has role of menu"
|
||||
);
|
||||
const subMenuChildren = subMenu.getAttributeValue("AXChildren");
|
||||
is(subMenuChildren.length, 4, "sub menu has 4 children");
|
||||
is(
|
||||
subMenu.getAttributeValue("AXVisibleChildren").length,
|
||||
4,
|
||||
"submenu has 4 visible children"
|
||||
);
|
||||
|
||||
// close context menu
|
||||
EventUtils.synthesizeKey("KEY_Escape");
|
||||
EventUtils.synthesizeKey("KEY_Escape");
|
||||
}
|
||||
);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user