Preload accessibility framework if not already loaded

Summary:
It looks like there are some edge cases when app did not load private accessibility framework (probably if it never set any accessibility values?)

This diff makes calls to accessibility hierarchy safer and ensures to preload framework if available

Reviewed By: lblasa

Differential Revision: D49501064

fbshipit-source-id: b46216b58bf6c9c63f900e199fea035e3262afb2
This commit is contained in:
Sash Zats
2023-09-21 10:14:43 -07:00
committed by Facebook GitHub Bot
parent 947cc819e3
commit 21d86c09af
3 changed files with 45 additions and 3 deletions

View File

@@ -8,6 +8,7 @@
#if FB_SONARKIT_ENABLED
#import "NSArray+Foundation.h"
#import "UIDAllyTraversal.h"
#import "UIDInitEvent+Foundation.h"
#import "UIDTraversalMode.h"
@@ -20,12 +21,15 @@ FB_LINKABLE(UIDInitEvent_Foundation)
@"frameworkEventMetadata" : self.frameworkEventMetadata
? [self.frameworkEventMetadata toFoundation]
: @[],
@"supportedTraversalModes" : @[
@"supportedTraversalModes" : UIDAllyTraversal.isSupported ? @[
NSStringFromUIDTraversalMode(UIDTraversalModeViewHierarchy),
NSStringFromUIDTraversalMode(UIDTraversalModeAccessibilityHierarchy),
] : @[
NSStringFromUIDTraversalMode(UIDTraversalModeViewHierarchy),
],
@"currentTraversalMode" :
NSStringFromUIDTraversalMode(self.currentTraversalMode),
@"currentTraversalMode" : NSStringFromUIDTraversalMode(
UIDAllyTraversal.isSupported ? self.currentTraversalMode
: UIDTraversalModeViewHierarchy),
};
}

View File

@@ -17,6 +17,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface UIDAllyTraversal : NSObject
@property(nonatomic, class, readonly, getter=isSupported) BOOL supported;
- (instancetype)initWithDescriptorRegister:
(UIDDescriptorRegister*)descriptorRegister;

View File

@@ -27,6 +27,13 @@
UIDDescriptorRegister* _descriptorRegister;
}
+ (BOOL)isSupported {
return _loadAccessibilityFramework() &&
[UIApplication.sharedApplication
respondsToSelector:@selector
(_accessibilityLeafDescendantsWithOptions:)];
}
- (instancetype)initWithDescriptorRegister:
(UIDDescriptorRegister*)descriptorRegister {
self = [super init];
@@ -41,10 +48,18 @@
return @[];
}
if (!_loadAccessibilityFramework()) {
return @[];
}
// create voice over representation of the app
id options = [NSClassFromString(@"UIAccessibilityElementTraversalOptions")
voiceOverOptionsIncludingElementsFromOpaqueProviders:YES
honorsGroups:NO];
if (![application respondsToSelector:@selector
(_accessibilityLeafDescendantsWithOptions:)]) {
return @[];
}
NSArray<NSObject*>* const allyNodes = [[application
_accessibilityLeafDescendantsWithOptions:options] mutableCopy];
@@ -78,6 +93,27 @@
return uidNode;
}
static BOOL _loadAccessibilityFramework(void) {
static BOOL isAccessibilityFrameworkLoaded;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURL* const knownFrameworkUrl =
[NSBundle bundleForClass:UIApplication.class].bundleURL;
if (!knownFrameworkUrl) {
isAccessibilityFrameworkLoaded = NO;
} else {
NSURL* const accessibilityFrameworkUrl =
[knownFrameworkUrl.URLByDeletingLastPathComponent
.URLByDeletingLastPathComponent
URLByAppendingPathComponent:
@"PrivateFrameworks/UIAccessibility.framework"];
isAccessibilityFrameworkLoaded =
[[NSBundle bundleWithURL:accessibilityFrameworkUrl] load];
}
});
return isAccessibilityFrameworkLoaded;
}
static NSString* _nameForNode(NSObject* node) {
NSMutableArray* const parts = [NSMutableArray new];
if (node.accessibilityLabel.length > 0) {