-
Notifications
You must be signed in to change notification settings - Fork 6k
Wires up accessibility bridge in mac embedding #23870
Conversation
|
|
||
| namespace flutter { // namespace | ||
|
|
||
| bool SystemVersionEqualTo(NSOperatingSystemVersion target) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These three methods seem like they should probably be in a commonly accessible target.
| std::unique_ptr<FlutterPlatformNodeDelegate> CreateFlutterPlatformNodeDelegate() override; | ||
|
|
||
| private: | ||
| FlutterEngine* _flutterEngine; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added ac omment below about this too - this seems like it wants to be a scoped_nsobject, or possibly some kind of weak_ptr. I'm not sure if that's used in the desktop.
It's an FML thing, but we might want to just move it to the darwin common instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took a look at the file in mac embedding, it looks like all of them are using raw ptr.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
macOS uses ARC. scoped_nsobject is not only unnecessary, but we do not currently have an ARC-safe scoped_nsobject implementation.
shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.mm
Show resolved
Hide resolved
stuartmorgan-g
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did a first pass, mostly since I was replying to comments anyway. I'll need to do a deeper pass to understanding the ownership models and interobject communication.
shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.mm
Show resolved
Hide resolved
| const ui::AXTreeData& GetAXTreeData() const; | ||
|
|
||
| //------------------------------------------------------------------------------ | ||
| /// @brief Get all pending accessibility events generated during |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Gets
| #pragma mark - NSAccessibility overrides | ||
|
|
||
| - (BOOL)isAccessibilityElement { | ||
| return self.accessibilityChildren != nil && [self.accessibilityChildren count] != 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first half of this check is redundant; [nil count] is 0.
|
|
||
| - (void)setupNotificationCenterObservers { | ||
| NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | ||
| // The MacOS fires this private message When voiceover turns on or off. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/The MacOS/macOS/
s/When voiceover/when VoiceOver/
| } | ||
| controller->_additionalKeyResponders = [[NSMutableOrderedSet alloc] init]; | ||
| controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; | ||
| [controller setupNotificationCenterObservers]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not call methods on an object during init. It's a common way to get subtle bugs over time. That's why this is CommonInit and not, say, -doCommonInitialization. If you want the code to be separate from this function for some reason, it should be in another, similar function.
| _engine.viewController = nil; | ||
| } | ||
|
|
||
| - (void)setupNotificationCenterObservers { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you keep this (as a function), note that "setup" is a noun, while the verb is "set up", so methods/functions should be capitalized accordingly. Also, this needs a declaration comment.
shell/platform/darwin/macos/framework/Source/FlutterViewController.mm
Outdated
Show resolved
Hide resolved
23e5f46 to
20c76f3
Compare
|
Hi @stuartmorgan @dnfield this is ready for another look |
|
A friendly bump |
shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h
Outdated
Show resolved
Hide resolved
| /// not provided, the function only compares | ||
| /// up to minor version. | ||
| bool isOperatingSystemAtLeastVersion(NSInteger majorVersion, | ||
| NSInteger minorVersion = -1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reason for the use of -1 for these as a special value, rather than just making the default zero? Is it just consistency with the other function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
zero is a valid version number, my intention is to use a invalid version number to be default to do a wild card compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the goal; my point is that comparing against 13, -1, -1 and against 13, 0, 0 have exactly the same effect for a >= check.
| /// a postive integer if provided. If it is | ||
| /// not provided, the function only compares | ||
| /// up to minor version. | ||
| bool isOperatingSystemAtMostVersion(NSInteger majorVersion, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this form? If we're putting an upper bound on a section of code, then we know the first OS version where that code is no longer needed. Why not just express everything that way and have a simpler API surface?
E.g., you have code that only runs for versions up through 10.13, which is equivalent to not running if it's 10.14+.
| NSCAssert(majorVersion != -1, @"majorVersion must not be ignored"); | ||
| NSCAssert(minorVersion != -1 || patchVersion == -1, | ||
| @"if minorVersion is ignored, patchVersion must also be ignored"); | ||
| NSOperatingSystemVersion systemVersion = [[NSProcessInfo processInfo] operatingSystemVersion]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted in the last review, NSProcessInfo has isOperatingSystemAtLeast.
In conjunction with the other comments (zero being fine as an unset value for >=, and <= being easily expressed with a different >=) that should mean that this entire file can be completely eliminated in favor of just using an existing OS method.
(And then I don't have to ask you where the tests for these functions are 😉 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah you are right
| controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; | ||
|
|
||
| NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | ||
| // The macOS fires this private message when voiceover turns on or off. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/The macOS/macOS/
557f997 to
e0fb2d3
Compare
c0fe185 to
3c75f62
Compare
|
A friendly bump |
I'm neck-deep in NNBD work at the moment; I can look some next week, or @cbracken can take over for me in reviewing. |
|
Taking a look on my end! |
3c75f62 to
c551aed
Compare
|
This is on @cbracken s review backlog. The presubmit failures are formatting issues. |
cbracken
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not finished, but sending out a first round of comments.
| NSRect window_bounds = [_engine.viewController.view convertRect:view_bounds toView:nil]; | ||
| NSRect global_bounds = [[_engine.viewController.view window] convertRectToScreen:window_bounds]; | ||
| // NSWindow is flipped. | ||
| NSScreen* screen = [[NSScreen screens] firstObject]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should figure this out/scrounge through docs and sample code, or reach out to the Chromium folks in the blame list. This seems pretty surprising to me. We make this sort of assumption (and shouldn't) in a few places on iOS, but it's likely to break less people there.
Can we use this instead?
https://developer.apple.com/documentation/appkit/nswindow/1419232-screen?language=objc
shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.h
Outdated
Show resolved
Hide resolved
| local_bounds.origin.y = bounds.y(); | ||
| local_bounds.size.width = bounds.width(); | ||
| local_bounds.size.height = bounds.height(); | ||
| // local_bounds is flipped against y axis. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we have a mix of platform experience on the team it would be good to provide the tiny bit of additional context that macOS Y co-ords start at bottom-left and increase towards top-right vs Flutter's top-left increasing to bottom-right.
shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.mm
Outdated
Show resolved
Hide resolved
shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMac.mm
Show resolved
Hide resolved
shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.h
Show resolved
Hide resolved
| /// @param[in] event_type The original event type. | ||
| /// @param[in] ax_node The original event target. | ||
| std::vector<NSAccessibilityEvent> ConvertEvent(ui::AXEventGenerator::Event event_type, | ||
| const ui::AXNode& ax_node) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe: MacOSEventFromAXEvent
| //--------------------------------------------------------------------------- | ||
| /// @brief Whether the given event is in current pending events. | ||
| /// @param[in] event_type The event you would like to look up. | ||
| bool IsInGeneratedEventBatch(ui::AXEventGenerator::Event event_type) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would bool HasPendingEvent be more accurate?
Why event_type rather than event?
| .user_info = nil, | ||
| }); | ||
| if ([[NSProcessInfo processInfo] | ||
| isOperatingSystemAtLeastVersion:{.majorVersion = 10, .minorVersion = 11}] && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just if (@available(macOS 10.11, *)).
| } | ||
| case ui::AXEventGenerator::Event::LIVE_REGION_CHANGED: { | ||
| if (![[NSProcessInfo processInfo] | ||
| isOperatingSystemAtLeastVersion:{.majorVersion = 10, .minorVersion = 14}]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@available(macOS 10.14, *)
| NSCAssert(flutter_engine_, @"Flutter engine should not be deallocated"); | ||
| auto bridge = flutter_engine_.accessibilityBridge.lock(); | ||
| NSCAssert(bridge, @"Accessibility bridge in flutter engine must not be null."); | ||
| std::vector<ui::AXEventGenerator::TargetedEvent> events = bridge->GetCurrentEvents(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: consider inlining bridge->GetCurrentEvents() into the for below, since this local is never accessed directly and the expression is short.
shell/platform/darwin/macos/framework/Source/AccessibilityBridgeMacDelegate.mm
Show resolved
Hide resolved
|
Hi @cbracken , This pr is ready for another look. |
| } | ||
| case ui::AXEventGenerator::Event::LIVE_REGION_CHANGED: { | ||
| if (@available(macOS 10.14, *)) { | ||
| } else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invert the check logic here and place this in the if block, then kill off the else block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got compiler error when i did
if (!@available(macOS 10.14, *)) {
}it seems like it has to be if (@available(..., ...)) inorder to use this annotation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah bummer. Would you mind adding a comment along the lines of // Do nothing on macOS 10.14 and later. just to make 100% clear this is not an oversight for future readers? The comment in the else block hints at this, but useful to add a comment.
| virtual ~FlutterPlatformNodeDelegateMac(); | ||
|
|
||
| //--------------------------------------------------------------------------- | ||
| /// @brief Gets the live region text of this node in UTF8 format. This is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit UTF-8
| /// macOS native accessibility event[s] | ||
| /// @param[in] event_type The original event type. | ||
| /// @param[in] ax_node The original event target. | ||
| std::vector<NSAccessibilityEvent> MacOSEventFromAXEvent(ui::AXEventGenerator::Event event_type, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a vector: MacOSEventsFromAXEvents. (Apologies for missing the 's'es in the original review comment)
| [engine setViewController:nil]; | ||
| [engine shutDownEngine]; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add a test for disabling a11y.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking much cleaner. Overall lgtm with a few nits.
I don't see any code doing a recursive walk of children in the semantics tree for hit-testing purposes -- though I do see the transform code. Is that not required on macOS, or are we doing that somewhere else?
Discussed some internal reference material for additional testing ideas over chat.
|
@cbracken |
|
This pull request is not suitable for automatic merging in its current state.
|
|
(I didn't re-review, but marking as approved since I trust that any remaining issues from my comments have been addressed in subsequent review) |
* Wires up accessibility bridge in mac embedding * addressing comment * addressing comments
* Wires up accessibility bridge in mac embedding * addressing comment * addressing comments
As title
tech overview: go/flutter-desktop-accessibility
detailed design: go/accessibility-bridge-detailed-design
umbrella issue
flutter/flutter#73819
Pre-launch Checklist
writing and running engine tests.
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.