mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Fix crash with CJK keyboard with emoji at end of text field (flutter/engine#42540)
The `isRTLAtPosition` method had a bug, it used `NSInteger max = [_selectionRects count]` instead of `NSInteger max = [_selectionRects count] - 1`. But I realized we don't even need the function any more, it was used in a few places in previous iterations of #36643, but in the only place remaining, we actually already have the selection rect and don't need to search for it by position. Btw as an explanation of the crash, I guess there is some mismatch between code point and character count somewhere. UIKit was asking for `caretRectForPosition:2` when we only had 1 character. This could have only crashed when floating cursor selection was used, but actually when switching to CJK keyboard, UIKit turns out to use `caretRectForPosition` to calculate something about the composing rect. Fixes https://github.com/flutter/flutter/issues/128031
This commit is contained in:
parent
ed72d25fef
commit
641003fc6d
@ -1658,25 +1658,6 @@ static BOOL IsSelectionRectBoundaryCloserToPoint(CGPoint point,
|
||||
return CGRectZero;
|
||||
}
|
||||
|
||||
- (BOOL)isRTLAtPosition:(NSUInteger)position {
|
||||
// _selectionRects is sorted by position already.
|
||||
// We can use binary search.
|
||||
NSInteger min = 0;
|
||||
NSInteger max = [_selectionRects count];
|
||||
while (min <= max) {
|
||||
const NSUInteger mid = min + (max - min) / 2;
|
||||
FlutterTextSelectionRect* rect = _selectionRects[mid];
|
||||
if (rect.position > position) {
|
||||
max = mid - 1;
|
||||
} else if (rect.position == position) {
|
||||
return rect.isRTL;
|
||||
} else {
|
||||
min = mid + 1;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (CGRect)caretRectForPosition:(UITextPosition*)position {
|
||||
NSInteger index = ((FlutterTextPosition*)position).index;
|
||||
UITextStorageDirection affinity = ((FlutterTextPosition*)position).affinity;
|
||||
@ -1699,7 +1680,8 @@ static BOOL IsSelectionRectBoundaryCloserToPoint(CGPoint point,
|
||||
CGRect characterAfterCaret = rects[0].rect;
|
||||
// Return a zero-width rectangle along the upstream edge of the character after the caret
|
||||
// position.
|
||||
if ([self isRTLAtPosition:index]) {
|
||||
if ([rects[0] isKindOfClass:[FlutterTextSelectionRect class]] &&
|
||||
((FlutterTextSelectionRect*)rects[0]).isRTL) {
|
||||
return CGRectMake(characterAfterCaret.origin.x + characterAfterCaret.size.width,
|
||||
characterAfterCaret.origin.y, 0, characterAfterCaret.size.height);
|
||||
} else {
|
||||
@ -1712,7 +1694,8 @@ static BOOL IsSelectionRectBoundaryCloserToPoint(CGPoint point,
|
||||
CGRect characterAfterCaret = rects[1].rect;
|
||||
// Return a zero-width rectangle along the upstream edge of the character after the caret
|
||||
// position.
|
||||
if ([self isRTLAtPosition:index]) {
|
||||
if ([rects[1] isKindOfClass:[FlutterTextSelectionRect class]] &&
|
||||
((FlutterTextSelectionRect*)rects[1]).isRTL) {
|
||||
return CGRectMake(characterAfterCaret.origin.x + characterAfterCaret.size.width,
|
||||
characterAfterCaret.origin.y, 0, characterAfterCaret.size.height);
|
||||
} else {
|
||||
@ -1727,7 +1710,8 @@ static BOOL IsSelectionRectBoundaryCloserToPoint(CGPoint point,
|
||||
// For both cases, return a zero-width rectangle along the downstream edge of the character
|
||||
// before the caret position.
|
||||
CGRect characterBeforeCaret = rects[0].rect;
|
||||
if ([self isRTLAtPosition:index - 1]) {
|
||||
if ([rects[0] isKindOfClass:[FlutterTextSelectionRect class]] &&
|
||||
((FlutterTextSelectionRect*)rects[0]).isRTL) {
|
||||
return CGRectMake(characterBeforeCaret.origin.x, characterBeforeCaret.origin.y, 0,
|
||||
characterBeforeCaret.size.height);
|
||||
} else {
|
||||
|
||||
@ -1558,6 +1558,29 @@ FLUTTER_ASSERT_ARC
|
||||
((FlutterTextPosition*)[inputView closestPositionToPoint:point withinRange:range]).affinity);
|
||||
}
|
||||
|
||||
- (void)testClosestPositionToPointWithPartialSelectionRects {
|
||||
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithOwner:textInputPlugin];
|
||||
[inputView setTextInputState:@{@"text" : @"COMPOSING"}];
|
||||
|
||||
[inputView setSelectionRects:@[ [FlutterTextSelectionRect
|
||||
selectionRectWithRect:CGRectMake(0, 0, 100, 100)
|
||||
position:0U] ]];
|
||||
// Asking with a position at the end of selection rects should give you the trailing edge of
|
||||
// the last rect.
|
||||
XCTAssertTrue(CGRectEqualToRect(
|
||||
[inputView caretRectForPosition:[FlutterTextPosition
|
||||
positionWithIndex:1
|
||||
affinity:UITextStorageDirectionForward]],
|
||||
CGRectMake(100, 0, 0, 100)));
|
||||
// Asking with a position beyond the end of selection rects should return CGRectZero without
|
||||
// crashing.
|
||||
XCTAssertTrue(CGRectEqualToRect(
|
||||
[inputView caretRectForPosition:[FlutterTextPosition
|
||||
positionWithIndex:2
|
||||
affinity:UITextStorageDirectionForward]],
|
||||
CGRectZero));
|
||||
}
|
||||
|
||||
#pragma mark - Floating Cursor - Tests
|
||||
|
||||
- (void)testFloatingCursorDoesNotThrow {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user