Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes ios accessibility send focus request to an unfocusable node #25738

Merged
merged 3 commits into from
May 3, 2021

Conversation

chunhtai
Copy link
Contributor

When we build a list view, we use cache extent to build some hidden element outside the view port so the voiceover can focus it and showOnScreen to move the hidden element into the screen to achieve scrolling. There is an issue if that hidden off screen element does not have any label/value/hint and thus not focusable when they are on screen. the accessibility bridge will still send the layoutchange to focus that element when it is scrolled to the viewport because it was previously focused. This PR fixes it so that when it send focus request, it will check again if that element is accessibility element and finds the first focusable child of the element if it is not. If none of the child is focusable, the layoutchange will be sent with nil target which lets the voiceover to decide what to focus next.

fixes flutter/flutter#80991

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.

@@ -284,7 +284,7 @@ - (BOOL)isAccessibilityElement {
// item that is currently off screen but the a11y navigation needs to know
// about.
return (([self node].flags & ~flutter::kScrollableSemanticsFlags) != 0 &&
[self node].flags != static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden)) ||
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 think this was a typo.

@@ -329,14 +333,13 @@ static bool DidFlagChange(const flutter::SemanticsNode& oldNode,
return object;
}

SemanticsObject* candidate = nil;
Copy link
Contributor Author

@chunhtai chunhtai Apr 23, 2021

Choose a reason for hiding this comment

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

I am not sure what had got into my head when i wrote this code. The pr change it to be more readable without changing the functionality

@chinmaygarde
Copy link
Member

Ping @gaaclarke for the review please?

@chunhtai
Copy link
Contributor Author

Also cc @goderbauer.
The current behavior will always treat offscreen element in the sliver list as focusable. Once it bring into view, it will then check its content whether this is actually focusable or not, and let the iOS to decide should it focus next. If there is a long list with unfocusable item, swipe right will scroll the list view while keep the focus at the same place. Is this acceptable?

@chunhtai
Copy link
Contributor Author

a friendly bump

@@ -284,7 +284,7 @@ - (BOOL)isAccessibilityElement {
// item that is currently off screen but the a11y navigation needs to know
// about.
return (([self node].flags & ~flutter::kScrollableSemanticsFlags) != 0 &&
[self node].flags != static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden)) ||
([self node].flags & ~static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden)) != 1) ||
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't look correct, why are you checking against the 1 value? Are you trying to check to see if the kIsHidden bit is turned on? It should look like what is above in that case: ([self node].flags & flutter::kScrollableSemanticsFlags) != 0

Checking anything against 1 is going to be fragile since it would depend on which bit is kIsHidden.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, I made a mistake, I surprised this still work

Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

Mostly looks good. The bitwise math looks a bit off. If it is right, it is fragile because of what a I mentioned in the comment. It occurred to me that maybe you wanted to check that it wasn't set? That would be like this: ([self node].flags & flutter::kScrollableSemanticsFlags) == 0

new flutter::MockAccessibilityBridge());
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
flutter::SemanticsNode node;
node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling) |
Copy link
Member

Choose a reason for hiding this comment

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

Are these casts necessary? They should already be int32_t's. (fwiw I think people use unsigned ints for flags to avoid 2's compliment weirdness)

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 got this error if i get rid of the static cast

error: invalid operands to binary expression ('flutter::SemanticsFlags' and 'flutter::SemanticsFlags')
  node.flags = flutter::SemanticsFlags::kHasImplicitScrolling |

Copy link
Member

Choose a reason for hiding this comment

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

weird, thanks

Comment on lines 225 to 227
if (nextToFocus) {
nextToFocus = FindFirstFocusable(nextToFocus);
} else if (root) {
Copy link
Member

Choose a reason for hiding this comment

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

Since you do these 2 checks I think it would be helpful if you pulled this out into a function like:

Object* FindFirstFocusable(Object* one, Object* two) {
  if (one) {
    return FindFirstFocusable(one);
  } else if (two) {
    return FindFirstFocusable(two);
  }
  return nullptr;
}

This function is a monster, the more duplicate logic we can pull out the better imo.

Logic seems find though.

@chunhtai
Copy link
Contributor Author

chunhtai commented May 3, 2021

@gaaclarke Thanks for reviewing, I addressed all comments, this is ready for another look.

Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for the fix!

@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 May 3, 2021
@fluttergithubbot fluttergithubbot merged commit 223fb2e into flutter:master May 3, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request May 4, 2021
gspencergoog pushed a commit to gspencergoog/engine that referenced this pull request May 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes platform-ios waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[iOS a11y] voiceover does not focus hidden children correctly in a scroll view
4 participants