mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 15:25:52 +00:00
Bug 1686164: Implement ignoreWithParent for context menu items and submenus. r=eeejay
Differential Revision: https://phabricator.services.mozilla.com/D101652
This commit is contained in:
parent
54cd3a3828
commit
fdab0ce3d7
@ -87,10 +87,10 @@
|
||||
- (NSString*)moxLabel;
|
||||
|
||||
// override
|
||||
- (NSArray*)moxChildren;
|
||||
- (NSArray*)moxVisibleChildren;
|
||||
|
||||
// override
|
||||
- (NSArray*)moxVisibleChildren;
|
||||
- (BOOL)moxIgnoreWithParent:(mozAccessible*)parent;
|
||||
|
||||
// override
|
||||
- (id)moxTitleUIElement;
|
||||
@ -101,6 +101,8 @@
|
||||
// override
|
||||
- (void)expire;
|
||||
|
||||
- (BOOL)isOpened;
|
||||
|
||||
@end
|
||||
|
||||
@interface mozMenuItemAccessible : mozSelectableChildAccessible
|
||||
@ -108,6 +110,9 @@
|
||||
// override
|
||||
- (NSString*)moxLabel;
|
||||
|
||||
// override
|
||||
- (BOOL)moxIgnoreWithParent:(mozAccessible*)parent;
|
||||
|
||||
// override
|
||||
- (NSString*)moxMenuItemMarkChar;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#import "mozSelectableElements.h"
|
||||
#import "MOXWebAreaAccessible.h"
|
||||
#import "MacUtils.h"
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsCocoaUtils.h"
|
||||
@ -18,7 +19,16 @@ using namespace mozilla::a11y;
|
||||
* Return the mozAccessibles that are selectable.
|
||||
*/
|
||||
- (NSArray*)selectableChildren {
|
||||
return [[self moxUnignoredChildren]
|
||||
NSArray* toFilter;
|
||||
if ([self isKindOfClass:[mozMenuAccessible class]]) {
|
||||
// If we are a menu, our children are only selectable if they are visible
|
||||
// so we filter this array instead of our unignored children list, which may
|
||||
// contain invisible items.
|
||||
toFilter = [static_cast<mozMenuAccessible*>(self) moxVisibleChildren];
|
||||
} else {
|
||||
toFilter = [self moxUnignoredChildren];
|
||||
}
|
||||
return [toFilter
|
||||
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
|
||||
mozAccessible* child,
|
||||
NSDictionary* bindings) {
|
||||
@ -38,14 +48,13 @@ using namespace mozilla::a11y;
|
||||
* Return the mozAccessibles that are actually selected.
|
||||
*/
|
||||
- (NSArray*)moxSelectedChildren {
|
||||
return [[self moxUnignoredChildren]
|
||||
return [[self selectableChildren]
|
||||
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
|
||||
mozAccessible* child,
|
||||
NSDictionary* bindings) {
|
||||
// Return mozSelectableChildAccessibles that have are selected (truthy
|
||||
// value).
|
||||
return [child isKindOfClass:[mozSelectableChildAccessible class]] &&
|
||||
[[(mozSelectableChildAccessible*)child moxSelected] boolValue];
|
||||
return [[(mozSelectableChildAccessible*)child moxSelected] boolValue];
|
||||
}]];
|
||||
}
|
||||
|
||||
@ -154,25 +163,43 @@ 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];
|
||||
- (BOOL)moxIgnoreWithParent:(mozAccessible*)parent {
|
||||
// This helps us generate the correct moxChildren array for
|
||||
// a sub menu -- that returned array should contain all
|
||||
// menu items, regardless of if they are visible or not.
|
||||
// Because moxChildren does ignore filtering, and because
|
||||
// our base ignore method filters out invisible accessibles,
|
||||
// we override this method.
|
||||
if ([parent isKindOfClass:[MOXWebAreaAccessible class]] ||
|
||||
[parent isKindOfClass:[MOXRootGroup class]]) {
|
||||
// We are a top level menu. Check our visibility the normal way
|
||||
return [super moxIgnoreWithParent:parent];
|
||||
}
|
||||
return nil;
|
||||
|
||||
if ([parent isKindOfClass:[mozMenuItemAccessible class]] &&
|
||||
[parent geckoAccessible].Role() == roles::PARENT_MENUITEM) {
|
||||
// We are a submenu. If our parent menu item is in an open menu
|
||||
// we should not be ignored
|
||||
id grandparent = [parent moxUnignoredParent];
|
||||
if ([grandparent isKindOfClass:[mozMenuAccessible class]]) {
|
||||
mozMenuAccessible* parentMenu =
|
||||
static_cast<mozMenuAccessible*>(grandparent);
|
||||
return ![parentMenu isOpened];
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, we call into our superclass's ignore method
|
||||
// to handle menus that are not submenus
|
||||
return [super moxIgnoreWithParent:parent];
|
||||
}
|
||||
|
||||
- (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.
|
||||
// (done in moxUnignoredChildren), and children which are visible (here).
|
||||
// We implement ignoreWithParent for both menus and menu items
|
||||
// to ensure moxUnignoredChildren returns a complete list of children
|
||||
// regardless of visibility, see comments in those methods for additional
|
||||
// info.
|
||||
return [[self moxChildren]
|
||||
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
|
||||
mozAccessible* child,
|
||||
@ -188,8 +215,7 @@ using namespace mozilla::a11y;
|
||||
|
||||
- (id)moxTitleUIElement {
|
||||
id parent = [self moxUnignoredParent];
|
||||
if ([parent isKindOfClass:[mozAccessible class]] &&
|
||||
[parent geckoAccessible].Role() == roles::PARENT_MENUITEM) {
|
||||
if (parent && [parent isKindOfClass:[mozAccessible class]]) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@ -216,6 +242,10 @@ using namespace mozilla::a11y;
|
||||
[super expire];
|
||||
}
|
||||
|
||||
- (BOOL)isOpened {
|
||||
return mIsOpened;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation mozMenuItemAccessible
|
||||
@ -224,6 +254,36 @@ using namespace mozilla::a11y;
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (BOOL)moxIgnoreWithParent:(mozAccessible*)parent {
|
||||
// This helps us generate the correct moxChildren array for
|
||||
// a mozMenuAccessible; the returned array should contain all
|
||||
// menu items, regardless of if they are visible or not.
|
||||
// Because moxChildren does ignore filtering, and because
|
||||
// our base ignore method filters out invisible accessibles,
|
||||
// we override this method.
|
||||
mozAccessible* grandparent =
|
||||
GetNativeFromGeckoAccessible([parent geckoAccessible].Parent());
|
||||
if ([grandparent isKindOfClass:[MOXWebAreaAccessible class]]) {
|
||||
return [parent moxIgnoreWithParent:grandparent];
|
||||
}
|
||||
|
||||
grandparent = [parent moxUnignoredParent];
|
||||
if ([grandparent isKindOfClass:[mozMenuItemAccessible class]]) {
|
||||
mozMenuItemAccessible* acc =
|
||||
static_cast<mozMenuItemAccessible*>(grandparent);
|
||||
if ([acc geckoAccessible].Role() == roles::PARENT_MENUITEM) {
|
||||
mozMenuAccessible* parentMenu = static_cast<mozMenuAccessible*>(parent);
|
||||
// if we are a menu item in a submenu, display only when
|
||||
// parent menu item is open
|
||||
return ![parentMenu isOpened];
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, we call into our superclass's method to handle
|
||||
// menuitems that are not within submenus
|
||||
return [super moxIgnoreWithParent:parent];
|
||||
}
|
||||
|
||||
- (NSString*)moxMenuItemMarkChar {
|
||||
Accessible* acc = mGeckoAccessible.AsAccessible();
|
||||
if (acc && acc->IsContent() &&
|
||||
|
@ -206,7 +206,8 @@ add_task(async () => {
|
||||
{ type: "contextmenu" },
|
||||
browser
|
||||
);
|
||||
await BrowserTestUtils.waitForPopupEvent(menu, "shown");
|
||||
await waitForMacEvent("AXMenuOpened");
|
||||
|
||||
menu = await getMacAccessible(menu);
|
||||
const menuChildren = menu.getAttributeValue("AXChildren");
|
||||
// menu contains 12 items and 3 splitters for 15 items total
|
||||
@ -236,24 +237,21 @@ add_task(async () => {
|
||||
// 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"
|
||||
menuChildren[1].getAttributeValue("AXVisibleChildren"),
|
||||
null,
|
||||
"Submenu 1 has no visible chldren when hidden"
|
||||
);
|
||||
is(
|
||||
menuChildren[11].getAttributeValue("AXChildren").length,
|
||||
0,
|
||||
"Submenu 2 has no chldren when hidden"
|
||||
menuChildren[11].getAttributeValue("AXVisibleChildren"),
|
||||
null,
|
||||
"Submenu 2 has no visible 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");
|
||||
await waitForMacEvent("AXMenuOpened");
|
||||
|
||||
// verify submenu-menuitem's attributes
|
||||
is(
|
||||
|
Loading…
Reference in New Issue
Block a user