Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@chunhtai
Copy link
Contributor

@chunhtai chunhtai commented Jan 22, 2021

As title

tech overview: go/flutter-desktop-accessibility

detailed design: go/accessibility-bridge-detailed-design

umbrella issue
flutter/flutter#73819

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test exempt. See testing the engine for instructions on
    writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the CLA.
  • All existing and new tests are passing.
  • The reviewer has submitted any presubmit flakes in this PR using the engine presubmit flakes form before re-triggering the failure.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@chunhtai
Copy link
Contributor Author

cc @dnfield @gw280


namespace flutter { // namespace

bool SystemVersionEqualTo(NSOperatingSystemVersion target) {
Copy link
Contributor

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;
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor

@stuartmorgan-g stuartmorgan-g left a 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.

const ui::AXTreeData& GetAXTreeData() const;

//------------------------------------------------------------------------------
/// @brief Get all pending accessibility events generated during
Copy link
Contributor

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;
Copy link
Contributor

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.
Copy link
Contributor

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];
Copy link
Contributor

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 {
Copy link
Contributor

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.

@chunhtai chunhtai added the Work in progress (WIP) Not ready (yet) for review! label Jan 25, 2021
@chunhtai chunhtai changed the title Wires up accessibility bridge in mac embedding [WIP]Wires up accessibility bridge in mac embedding Jan 25, 2021
@chunhtai chunhtai removed the Work in progress (WIP) Not ready (yet) for review! label Jan 26, 2021
@chunhtai chunhtai changed the title [WIP]Wires up accessibility bridge in mac embedding Wires up accessibility bridge in mac embedding Jan 26, 2021
@chunhtai
Copy link
Contributor Author

Hi @stuartmorgan @dnfield this is ready for another look

@chunhtai
Copy link
Contributor Author

chunhtai commented Feb 1, 2021

A friendly bump

/// not provided, the function only compares
/// up to minor version.
bool isOperatingSystemAtLeastVersion(NSInteger majorVersion,
NSInteger minorVersion = -1,
Copy link
Contributor

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?

Copy link
Contributor Author

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

Copy link
Contributor

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,
Copy link
Contributor

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];
Copy link
Contributor

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 😉 )

Copy link
Contributor Author

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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/The macOS/macOS/

@chunhtai chunhtai force-pushed the mac_embedding branch 2 times, most recently from 557f997 to e0fb2d3 Compare February 5, 2021 18:54
@chunhtai chunhtai requested a review from cbracken February 10, 2021 20:14
@chunhtai
Copy link
Contributor Author

A friendly bump

@stuartmorgan-g
Copy link
Contributor

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.

@cbracken
Copy link
Member

Taking a look on my end!

@chinmaygarde
Copy link
Member

This is on @cbracken s review backlog. The presubmit failures are formatting issues.

Copy link
Member

@cbracken cbracken left a 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];
Copy link
Member

@cbracken cbracken Feb 26, 2021

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

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.
Copy link
Member

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.

/// @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;
Copy link
Member

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;
Copy link
Member

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}] &&
Copy link
Member

@cbracken cbracken Feb 26, 2021

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}]) {
Copy link
Member

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();
Copy link
Member

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.

@chunhtai
Copy link
Contributor Author

chunhtai commented Mar 3, 2021

Hi @cbracken , This pr is ready for another look.

@chunhtai chunhtai requested a review from cbracken March 4, 2021 18:24
}
case ui::AXEventGenerator::Event::LIVE_REGION_CHANGED: {
if (@available(macOS 10.14, *)) {
} else {
Copy link
Member

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.

Copy link
Contributor Author

@chunhtai chunhtai Mar 9, 2021

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?

Copy link
Member

@cbracken cbracken Mar 9, 2021

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
Copy link
Member

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,
Copy link
Member

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];
}

Copy link
Member

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.

Copy link
Member

@cbracken cbracken left a 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.

@chunhtai
Copy link
Contributor Author

chunhtai commented Mar 9, 2021

@cbracken
voiceover does not have hittest, in mac VoiceOver, the mouse behave exactly as before, the only way to control the accessibility focus is using keyboard.

@chunhtai chunhtai added the waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land. label Mar 10, 2021
@fluttergithubbot
Copy link
Contributor

This pull request is not suitable for automatic merging in its current state.

  • This pull request has changes requested by @dnfield. Please resolve those before re-applying the label.
  • This pull request has changes requested by @stuartmorgan. Please resolve those before re-applying the label.

@fluttergithubbot fluttergithubbot removed the waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land. label Mar 10, 2021
@stuartmorgan-g
Copy link
Contributor

(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)

@chunhtai chunhtai merged commit afc72ee into flutter:master Mar 10, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Mar 10, 2021
hjfreyer pushed a commit to hjfreyer/engine that referenced this pull request Mar 22, 2021
* Wires up accessibility bridge in mac embedding

* addressing comment

* addressing comments
chriscraws pushed a commit to chriscraws/engine that referenced this pull request Mar 23, 2021
* Wires up accessibility bridge in mac embedding

* addressing comment

* addressing comments
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants