Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workaround iOS text input crash for emoji+Korean text #36295

Merged
merged 2 commits into from
Oct 4, 2022

Conversation

cyanglaz
Copy link
Contributor

@cyanglaz cyanglaz commented Sep 20, 2022

This is what iOS 16 does when transforming 2 Korean characters into 1 (for example ㅇ ㅏ) right after a unicode composed character (emoji etc):

  1. The keyboard deletes the emoji.
  2. The keyboard API inserts the composed character back.
  3. The keyboard API inserts the transformed Korean character.

However, in iOS 16.0, there is a bug that in step 2, only the first unicode is inserted, which creates an incomplete or different composed character.

To workaround this issue, this PR:

  1. Cache the deleted composed character.
  2. Detects when iOS only adds the first unicode of the composed character, then replaced the first unicode with the cached input.

The issue is caused by an iOS bug and this PR only provides a workaround, we should revert this workaround when the issue is fixed by iOS.

fixes flutter/flutter#111494

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See testing the engine for instructions on
    writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the CLA.
  • All existing and new tests are passing.

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

if (emoji) {
self.lastEmoji = emoji;
}
if (text.length == 1 && !text.UTF8String &&

This comment was marked as outdated.

dispatch_once(&onceToken, ^{
set = CFCharacterSetCreateMutableCopy(
kCFAllocatorDefault,
CTFontCopyCharacterSet(CTFontCreateWithName(CFSTR("AppleColorEmoji"), 0.0, NULL)));

This comment was marked as outdated.

@cyanglaz
Copy link
Contributor Author

#34508 seems similar and we might be able to use some of the tests in the PR.

@jmagman
Copy link
Member

jmagman commented Sep 21, 2022

- (void)testDeletingBackward {
NSDictionary* config = self.mutableTemplateCopy;
[self setClientId:123 configuration:config];
NSArray<FlutterTextInputView*>* inputFields = self.installedInputViews;
FlutterTextInputView* inputView = inputFields[0];
[inputView insertText:@"ឹ😀 text 🥰👨‍👩‍👧‍👦🇺🇳ดี "];
[inputView deleteBackward];
[inputView deleteBackward];
// Thai vowel is removed.
XCTAssertEqualObjects(inputView.text, @"ឹ😀 text 🥰👨‍👩‍👧‍👦🇺🇳ด");
[inputView deleteBackward];
XCTAssertEqualObjects(inputView.text, @"ឹ😀 text 🥰👨‍👩‍👧‍👦🇺🇳");
[inputView deleteBackward];
XCTAssertEqualObjects(inputView.text, @"ឹ😀 text 🥰👨‍👩‍👧‍👦");
[inputView deleteBackward];
XCTAssertEqualObjects(inputView.text, @"ឹ😀 text 🥰");
[inputView deleteBackward];

Copy link
Contributor

@LongCatIsLooong LongCatIsLooong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach LGTM. I think the keyboard also handles grapheme clusters wrong. I'll check what the exact behavior is.

}
// Only need to use lastComposedCharacters for the one insertion right after deletion.
// Clear it even it is not used in the above if block.
self.lastComposedCharacters = nil;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also maybe set the stored value to nil at the end of the runloop?

// Workaround for https://github.com/flutter/flutter/issues/111494
// TODO(cyanglaz): revert this workaround if when flutter supports a minimum iOS version which
// this bug is fixed by Apple.
text = self.lastComposedCharacters;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this step adds the emoji back, then the IME will call insertText: again to insert "어"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the text.length == 1 check? Shouldn't !text.UTF8String be enough (I'm assuming this checks the string is not malformed)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm. I tried some grapheme clusters, the keyboard always inserts the first code unit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this step adds the emoji back, then the IME will call insertText: again to insert "어"?

Yes. I'm going to add what the "bugged" behavior actually is in the PR description.

@cyanglaz cyanglaz marked this pull request as ready for review September 23, 2022 20:10
// Workaround for https://github.com/flutter/flutter/issues/111494
// TODO(cyanglaz): revert this workaround if when flutter supports a minimum iOS version which
// this bug is fixed by Apple.
text = self.lastComposedCharacters;
text = self.temporarilyDeletedComposedCharacter;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: also clear the stored string here since it's consumed?

[inputView deleteBackward];
[inputView shouldChangeTextInRange:OCMClassMock([UITextRange class]) replacementText:@""];

// Insert the first unicode in the emoji.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: first unicode -> first unichar

@@ -1956,9 +1961,8 @@ - (void)deleteBackward {
NSString* deletedText = [self.text substringWithRange:_selectedTextRange.range];
CFRange range =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider using fml::RangeForCharacterAtIndex

@jmagman
Copy link
Member

jmagman commented Sep 28, 2022

@cyanglaz is out this week, he will pick the nits for next week.

format fix

fix

fix delete multiple

remove unused code

add comments

format

fix

comments

fix

clang tidy fix

autorelease

tests

release temporarilyDeletedComposedCharacter after use, use more accurate verbose
@jmagman
Copy link
Member

jmagman commented Oct 4, 2022

@cyanglaz is this ready to merge?

@cyanglaz
Copy link
Contributor Author

cyanglaz commented Oct 4, 2022

@LongCatIsLooong Updated to use fml::RangeForCharacterAtIndex, also refactored out an "IsEmoji" method, ptal

range:charRange
remainingRange:NULL];
if (gotCodePoint && u_hasBinaryProperty(codePoint, UCHAR_EMOJI)) {
if (IsEmoji(self.text, charRange)) {
Copy link
Contributor

@LongCatIsLooong LongCatIsLooong Oct 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: some emojis are in plane 0 and require only one code point to encode, e.g.: https://www.compart.com/en/unicode/U+2764

Copy link
Contributor

@LongCatIsLooong LongCatIsLooong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still LGTM

@cyanglaz cyanglaz added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 4, 2022
@auto-submit auto-submit bot merged commit ba1c87a into flutter:main Oct 4, 2022
@cyanglaz cyanglaz deleted the textfield_crash branch October 4, 2022 22:57
thor-ola added a commit to olaparty/engine-builds that referenced this pull request Oct 5, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Oct 5, 2022
cyanglaz pushed a commit to cyanglaz/engine that referenced this pull request Oct 5, 2022
jmagman pushed a commit to jmagman/engine that referenced this pull request Oct 17, 2022
cyanglaz pushed a commit to cyanglaz/engine that referenced this pull request Oct 17, 2022
cyanglaz pushed a commit to cyanglaz/engine that referenced this pull request Oct 17, 2022
godofredoc pushed a commit that referenced this pull request Oct 17, 2022
* Fix an issue that deleting an emoji may crash the app (#34508)

* Workaround iOS text input crash for emoji+Korean text (#36295)

Co-authored-by: Yang Chao <[email protected]>
Co-authored-by: Chris Yang <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App platform-ios
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[iOS 16] Flutter apps crash when emojis and text are combined.
3 participants