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

Commit 5d8ee52

Browse files
authored
macOS: Fix crash in attributedSubstringForProposedRange with out of bounds range (#54469)
Fixes flutter/flutter#153157 *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## 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] and the [C++, Objective-C, Java style guides]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I signed the [CLA]. - [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/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 4887a4d commit 5d8ee52

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.mm

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -904,10 +904,14 @@ - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
904904
if (_activeModel == nullptr) {
905905
return nil;
906906
}
907+
NSString* text = [NSString stringWithUTF8String:_activeModel->GetText().c_str()];
908+
if (range.location >= text.length) {
909+
return nil;
910+
}
911+
range.length = std::min(range.length, text.length - range.location);
907912
if (actualRange != nil) {
908913
*actualRange = range;
909914
}
910-
NSString* text = [NSString stringWithUTF8String:_activeModel->GetText().c_str()];
911915
NSString* substring = [text substringWithRange:range];
912916
return [[NSAttributedString alloc] initWithString:substring attributes:nil];
913917
}

shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,6 +1958,54 @@ - (bool)testSelectorsNotForwardedToFrameworkIfNoClient {
19581958
ASSERT_TRUE([[FlutterInputPluginTestObjc alloc] testSendActionDoNotInsertNewLine]);
19591959
}
19601960

1961+
TEST(FlutterTextInputPluginTest, TestAttributedSubstringOutOfRange) {
1962+
id engineMock = flutter::testing::CreateMockFlutterEngine(@"");
1963+
id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
1964+
OCMStub( // NOLINT(google-objc-avoid-throwing-exception)
1965+
[engineMock binaryMessenger])
1966+
.andReturn(binaryMessengerMock);
1967+
1968+
FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
1969+
nibName:@""
1970+
bundle:nil];
1971+
1972+
FlutterTextInputPlugin* plugin =
1973+
[[FlutterTextInputPlugin alloc] initWithViewController:viewController];
1974+
1975+
NSDictionary* setClientConfig = @{
1976+
@"inputAction" : @"action",
1977+
@"enableDeltaModel" : @"true",
1978+
@"inputType" : @{@"name" : @"inputName"},
1979+
};
1980+
[plugin handleMethodCall:[FlutterMethodCall methodCallWithMethodName:@"TextInput.setClient"
1981+
arguments:@[ @(1), setClientConfig ]]
1982+
result:^(id){
1983+
}];
1984+
1985+
FlutterMethodCall* call = [FlutterMethodCall methodCallWithMethodName:@"TextInput.setEditingState"
1986+
arguments:@{
1987+
@"text" : @"Text",
1988+
@"selectionBase" : @(0),
1989+
@"selectionExtent" : @(0),
1990+
@"composingBase" : @(-1),
1991+
@"composingExtent" : @(-1),
1992+
}];
1993+
1994+
[plugin handleMethodCall:call
1995+
result:^(id){
1996+
}];
1997+
1998+
NSRange out;
1999+
NSAttributedString* text = [plugin attributedSubstringForProposedRange:NSMakeRange(1, 10)
2000+
actualRange:&out];
2001+
EXPECT_TRUE([text.string isEqualToString:@"ext"]);
2002+
EXPECT_EQ(out.location, 1u);
2003+
EXPECT_EQ(out.length, 3u);
2004+
2005+
text = [plugin attributedSubstringForProposedRange:NSMakeRange(4, 10) actualRange:&out];
2006+
EXPECT_EQ(text, nil);
2007+
}
2008+
19612009
TEST(FlutterTextInputPluginTest, CanWorkWithFlutterTextField) {
19622010
FlutterEngine* engine = CreateTestEngine();
19632011
FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engine

0 commit comments

Comments
 (0)