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

Commit 80dbeb3

Browse files
justinmcmehmetf
authored andcommitted
iOS Text Editing Infinite Loop (#20160)
Fixes an infinite loop by eliminating an unnecessary engine/framework message.
1 parent 3a73d07 commit 80dbeb3

File tree

2 files changed

+30
-64
lines changed

2 files changed

+30
-64
lines changed

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

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -537,10 +537,7 @@ - (void)setTextInputClient:(int)client {
537537
_textInputClient = client;
538538
}
539539

540-
// Return true if the new input state needs to be synced back to the framework.
541-
// TODO(LongCatIsLooong): setTextInputState should never call updateEditingState. Sending the
542-
// editing value back may overwrite the framework's updated editing value.
543-
- (BOOL)setTextInputState:(NSDictionary*)state {
540+
- (void)setTextInputState:(NSDictionary*)state {
544541
NSString* newText = state[@"text"];
545542
BOOL textChanged = ![self.text isEqualToString:newText];
546543
if (textChanged) {
@@ -575,9 +572,6 @@ - (BOOL)setTextInputState:(NSDictionary*)state {
575572
if (textChanged) {
576573
[self.inputDelegate textDidChange:self];
577574
}
578-
579-
// For consistency with Android behavior, send an update to the framework if the text changed.
580-
return textChanged;
581575
}
582576

583577
// Extracts the selection information from the editing state dictionary.
@@ -1423,9 +1417,7 @@ - (void)addToInputParentViewIfNeeded:(FlutterTextInputView*)inputView {
14231417
}
14241418

14251419
- (void)setTextInputEditingState:(NSDictionary*)state {
1426-
if ([_activeView setTextInputState:state]) {
1427-
[_activeView updateEditingState];
1428-
}
1420+
[_activeView setTextInputState:state];
14291421
}
14301422

14311423
- (void)clearTextInputClient {

shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m

Lines changed: 28 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ @interface FlutterTextInputView ()
1616
@property(nonatomic, copy) NSString* autofillId;
1717

1818
- (void)setEditableTransform:(NSArray*)matrix;
19-
- (BOOL)setTextInputState:(NSDictionary*)state;
19+
- (void)setTextInputState:(NSDictionary*)state;
2020
- (void)setMarkedRect:(CGRect)markedRect;
2121
- (void)updateEditingState;
2222
- (BOOL)isVisibleToAutofill;
@@ -211,71 +211,45 @@ - (void)testUITextInputCallsUpdateEditingStateOnce {
211211
XCTAssertEqual(updateCount, 6);
212212
}
213213

214-
- (void)testTextChangesTriggerUpdateEditingClient {
214+
- (void)testTextChangesDoNotTriggerUpdateEditingClient {
215215
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init];
216216
inputView.textInputDelegate = engine;
217217

218-
[inputView.text setString:@"BEFORE"];
219-
inputView.markedTextRange = nil;
220-
inputView.selectedTextRange = nil;
221-
222-
// Text changes trigger update.
223-
XCTAssertTrue([inputView setTextInputState:@{@"text" : @"AFTER"}]);
224-
225-
// Don't send anything if there's nothing new.
226-
XCTAssertFalse([inputView setTextInputState:@{@"text" : @"AFTER"}]);
227-
}
218+
__block int updateCount = 0;
219+
OCMStub([engine updateEditingClient:0 withState:[OCMArg isNotNil]])
220+
.andDo(^(NSInvocation* invocation) {
221+
updateCount++;
222+
});
228223

229-
- (void)testSelectionChangeDoesNotTriggerUpdateEditingClient {
230-
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init];
231-
inputView.textInputDelegate = engine;
224+
[inputView.text setString:@"BEFORE"];
225+
XCTAssertEqual(updateCount, 0);
232226

233-
[inputView.text setString:@"SELECTION"];
234227
inputView.markedTextRange = nil;
235228
inputView.selectedTextRange = nil;
229+
XCTAssertEqual(updateCount, 1);
236230

237-
BOOL shouldUpdate = [inputView
238-
setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @0, @"selectionExtent" : @3}];
239-
XCTAssertFalse(shouldUpdate);
231+
// Text changes don't trigger an update.
232+
XCTAssertEqual(updateCount, 1);
233+
[inputView setTextInputState:@{@"text" : @"AFTER"}];
234+
XCTAssertEqual(updateCount, 1);
235+
[inputView setTextInputState:@{@"text" : @"AFTER"}];
236+
XCTAssertEqual(updateCount, 1);
240237

241-
shouldUpdate = [inputView
238+
// Selection changes don't trigger an update.
239+
[inputView
240+
setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @0, @"selectionExtent" : @3}];
241+
XCTAssertEqual(updateCount, 1);
242+
[inputView
242243
setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @3}];
243-
XCTAssertFalse(shouldUpdate);
244-
245-
shouldUpdate = [inputView
246-
setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @2}];
247-
XCTAssertFalse(shouldUpdate);
248-
249-
// Don't send anything if there's nothing new.
250-
shouldUpdate = [inputView
251-
setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @2}];
252-
XCTAssertFalse(shouldUpdate);
253-
}
254-
255-
- (void)testComposingChangeDoesNotTriggerUpdateEditingClient {
256-
FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init];
257-
inputView.textInputDelegate = engine;
258-
259-
// Reset to test marked text.
260-
[inputView.text setString:@"COMPOSING"];
261-
inputView.markedTextRange = nil;
262-
inputView.selectedTextRange = nil;
263-
264-
BOOL shouldUpdate = [inputView
265-
setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @0, @"composingExtent" : @3}];
266-
XCTAssertFalse(shouldUpdate);
267-
268-
shouldUpdate = [inputView
269-
setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @3}];
270-
XCTAssertFalse(shouldUpdate);
271-
272-
shouldUpdate = [inputView
273-
setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @2}];
274-
XCTAssertFalse(shouldUpdate);
244+
XCTAssertEqual(updateCount, 1);
275245

276-
shouldUpdate = [inputView
246+
// Composing region changes don't trigger an update.
247+
[inputView
277248
setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @2}];
278-
XCTAssertFalse(shouldUpdate);
249+
XCTAssertEqual(updateCount, 1);
250+
[inputView
251+
setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @3}];
252+
XCTAssertEqual(updateCount, 1);
279253
}
280254

281255
- (void)testUITextInputAvoidUnnecessaryUndateEditingClientCalls {

0 commit comments

Comments
 (0)