Avoid iOS text selection crash by returning nil range (#161996)

[Previously approved](https://github.com/flutter/engine/pull/55909) in
/engine before monorepo shift.

Reverts an unnecessary assertion introduced by
[#16496](https://github.com/flutter/engine/pull/16496) that crashes upon
valid user text field interactions. Follow-on work can resolve the
underlying discrepancy between iOS and Flutter range finding (see
#160100). For crash reports, see issue
[#138464](https://github.com/flutter/flutter/issues/138464).

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

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

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

Co-authored-by: Chris Bracken <chris@bracken.jp>
This commit is contained in:
Ryan Ferrell 2025-01-28 04:10:19 +09:00 committed by GitHub
parent ca14403b06
commit 489b186904
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 29 additions and 2 deletions

View File

@ -1293,7 +1293,14 @@ static BOOL IsSelectionRectBoundaryCloserToPoint(CGPoint point,
NSAssert([range isKindOfClass:[FlutterTextRange class]],
@"Expected a FlutterTextRange for range (got %@).", [range class]);
NSRange textRange = ((FlutterTextRange*)range).range;
NSAssert(textRange.location != NSNotFound, @"Expected a valid text range.");
if (textRange.location == NSNotFound) {
// Avoids [crashes](https://github.com/flutter/flutter/issues/138464) from an assertion
// against NSNotFound.
// TODO(hellohuanlin): This is a temp workaround, but we should look into why
// framework is providing NSNotFound to the engine.
// https://github.com/flutter/flutter/issues/160100
return nil;
}
// Sanitize the range to prevent going out of bounds.
NSUInteger location = MIN(textRange.location, self.text.length);
NSUInteger length = MIN(self.text.length - location, textRange.length);

View File

@ -530,6 +530,19 @@ FLUTTER_ASSERT_ARC
XCTAssertEqual(substring.length, 0ul);
}
- (void)testTextInRangeAcceptsNSNotFoundLocationGracefully {
NSDictionary* config = self.mutableTemplateCopy;
[self setClientId:123 configuration:config];
NSArray<FlutterTextInputView*>* inputFields = self.installedInputViews;
FlutterTextInputView* inputView = inputFields[0];
[inputView insertText:@"text"];
UITextRange* range = [FlutterTextRange rangeWithNSRange:NSMakeRange(NSNotFound, 0)];
NSString* substring = [inputView textInRange:range];
XCTAssertNil(substring);
}
- (void)testStandardEditActions {
NSDictionary* config = self.mutableTemplateCopy;
[self setClientId:123 configuration:config];

View File

@ -42,7 +42,14 @@ static const UIAccessibilityTraits kUIAccessibilityTraitUndocumentedEmptyLine =
NSAssert([range isKindOfClass:[FlutterTextRange class]],
@"Expected a FlutterTextRange for range (got %@).", [range class]);
NSRange textRange = ((FlutterTextRange*)range).range;
NSAssert(textRange.location != NSNotFound, @"Expected a valid text range.");
if (textRange.location == NSNotFound) {
// Avoids [crashes](https://github.com/flutter/flutter/issues/138464) from an assertion
// against NSNotFound.
// TODO(hellohuanlin): This is a temp workaround, but we should look into why
// framework is providing NSNotFound to the engine.
// https://github.com/flutter/flutter/issues/160100
return nil;
}
return [self.text substringWithRange:textRange];
}