mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Fix: a11y crash when an accessible link is ellipsized away (#37050)
Summary: If an accessible link is ellipsized out of being rendered, the AccessibilityDelegate will still attempt to populate an accessibility node for it; doing so results in an invalid request to a TextLayout API, however, causing a crash. This crash occurs as soon as the element is rendered, so long as a Screen Reader (or app using similar a11y APIs) is enabled. This change uses a technique similar to those existing to make the node "blank" in such cases, so Talkback can filter it out—and, more importantly, not crash. ## Changelog: [Android] [Fixed] - Fix links hidden via ellipsis crashing screen readers Pull Request resolved: https://github.com/facebook/react-native/pull/37050 Test Plan: - Added a block to the "Accessibility Android APIs" page in the rn-tester app. Without the changes to `ReactAccessibilityDelegate`, this component crashes the app; with the changes, the component renders without problem. - You can also see the crash "in the wild" using [this Expo Snack](https://snack.expo.dev/dhleong/2d1407) that I put together when trying to isolate this issue. Reviewed By: rshest Differential Revision: D46206673 Pulled By: NickGerleman fbshipit-source-id: 0eb3e735202ee6be5f931bbb4bb92c24e7458ea6
This commit is contained in:
committed by
Facebook GitHub Bot
parent
fd9e295bef
commit
d54f486fe6
+21
-4
@@ -786,9 +786,18 @@ public class ReactAccessibilityDelegate extends ExploreByTouchHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: The span may not actually have visible bounds within its parent,
|
||||
// due to line limits, etc.
|
||||
final Rect bounds = getBoundsInParent(accessibleTextSpan);
|
||||
if (bounds == null) {
|
||||
node.setContentDescription("");
|
||||
node.setBoundsInParent(new Rect(0, 0, 1, 1));
|
||||
return;
|
||||
}
|
||||
|
||||
node.setContentDescription(accessibleTextSpan.description);
|
||||
node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
|
||||
node.setBoundsInParent(getBoundsInParent(accessibleTextSpan));
|
||||
node.setBoundsInParent(bounds);
|
||||
node.setRoleDescription(mView.getResources().getString(R.string.link_description));
|
||||
node.setClassName(AccessibilityRole.getValue(AccessibilityRole.BUTTON));
|
||||
}
|
||||
@@ -805,10 +814,19 @@ public class ReactAccessibilityDelegate extends ExploreByTouchHelper {
|
||||
return new Rect(0, 0, textView.getWidth(), textView.getHeight());
|
||||
}
|
||||
|
||||
Rect rootRect = new Rect();
|
||||
|
||||
double startOffset = accessibleLink.start;
|
||||
double endOffset = accessibleLink.end;
|
||||
|
||||
// Ensure the link hasn't been ellipsized away; in such cases,
|
||||
// getPrimaryHorizontal will crash (and the link isn't rendered anyway).
|
||||
int startOffsetLineNumber = textViewLayout.getLineForOffset((int) startOffset);
|
||||
int lineEndOffset = textViewLayout.getLineEnd(startOffsetLineNumber);
|
||||
if (startOffset > lineEndOffset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Rect rootRect = new Rect();
|
||||
|
||||
double startXCoordinates = textViewLayout.getPrimaryHorizontal((int) startOffset);
|
||||
|
||||
final Paint paint = new Paint();
|
||||
@@ -818,7 +836,6 @@ public class ReactAccessibilityDelegate extends ExploreByTouchHelper {
|
||||
paint.setTextSize(textSize);
|
||||
int textWidth = (int) Math.ceil(paint.measureText(accessibleLink.description));
|
||||
|
||||
int startOffsetLineNumber = textViewLayout.getLineForOffset((int) startOffset);
|
||||
int endOffsetLineNumber = textViewLayout.getLineForOffset((int) endOffset);
|
||||
boolean isMultiline = startOffsetLineNumber != endOffsetLineNumber;
|
||||
textViewLayout.getLineBounds(startOffsetLineNumber, rootRect);
|
||||
|
||||
@@ -65,6 +65,20 @@ class AccessibilityAndroidExample extends React.Component<
|
||||
render(): React.Node {
|
||||
return (
|
||||
<RNTesterPage title={'Accessibility Android APIs'}>
|
||||
<RNTesterBlock title="Ellipsized Accessible Links">
|
||||
<Text numberOfLines={3}>
|
||||
<Text>
|
||||
Bacon {this.state.count} Ipsum{'\n'}
|
||||
</Text>
|
||||
<Text>Dolor sit amet{'\n'}</Text>
|
||||
<Text>Eggsecetur{'\n'}</Text>
|
||||
<Text>{'\n'}</Text>
|
||||
<Text accessibilityRole="link" onPress={this._addOne}>
|
||||
http://github.com
|
||||
</Text>
|
||||
</Text>
|
||||
</RNTesterBlock>
|
||||
|
||||
<RNTesterBlock title="LiveRegion">
|
||||
<TouchableWithoutFeedback onPress={this._addOne}>
|
||||
<View style={styles.embedded}>
|
||||
|
||||
Reference in New Issue
Block a user