Skip to content

Fix the breaking of multi-code-unit characters in obscure mode - #123366

Merged
auto-submit[bot] merged 26 commits into
flutter:masterfrom
justinmc:obscure-emojis
May 23, 2023
Merged

auto-submit[bot] merged 26 commits into
flutter:masterfrom
justinmc:obscure-emojis

Conversation

@justinmc

@justinmc justinmc commented Mar 23, 2023 •

Copy link
Copy Markdown
Contributor

This PR changes the character boundary behavior of obscured fields to be based on code points instead of code units.

So it used to be possible to traverse and delete obscured text inside of code points (and breaking a code point like that would cause a crash):

output2

But now moving the cursor and deleting is based on code points:

output1

Native behavior

Native iOS deletes part of the emoji, native Mac deletes the whole emoji. See #122381 (comment). So it's unclear what the desired behavior should actually be.

Resources

Fixes #122381

I thought this might not fix the case where a broken emoji is directly pasted into the field, but it seems to work by trying this: �‍👩‍👦‍👦

CC @LongCatIsLooong

@justinmc
justinmc requested a review from Renzo-Olivares March 23, 2023 23:46
@flutter-dashboard flutter-dashboard Bot added a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. labels Mar 23, 2023
@justinmc justinmc self-assigned this Mar 23, 2023
@Renzo-Olivares

Renzo-Olivares commented Mar 27, 2023 •

Copy link
Copy Markdown
Contributor

It makes sense to me that we should remove the entire emoji when pressing delete since I'm not sure what a user would expect if only a portion of it is deleted in a password field. I do wonder why _CodeUnitBoundary was used vs CharacterBoundary in the case on an obscure field maybe there are some cases where it is needed. Looking at the failing tests it looks like this causes some unexpected changes.

        const String testCluster = '👨‍👩‍👦👨‍👩‍👦👨‍👩‍👦'; // 8 * 3
        const LogicalKeyboardKey trigger = LogicalKeyboardKey.delete;
        testWidgets('cross-cluster obscured text', (WidgetTester tester) async {
          controller.text = testCluster;
          controller.selection = const TextSelection(
            baseOffset: 1,
            extentOffset: 9,
          );

          await tester.pumpWidget(buildEditableText(obscured: true));
          await sendKeyCombination(tester, const SingleActivator(trigger));

          expect(
            controller.text,
            '👨‍👩‍👦👨‍👩‍👦',
          );

          expect(
            controller.selection,
            const TextSelection.collapsed(offset: 1),
          );
        }, variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS }));
      });

Though looking at this test, it looks like we already expect that delete deletes the entire grapheme cluster.

@LongCatIsLooong

LongCatIsLooong commented Mar 28, 2023 •

Copy link
Copy Markdown
Contributor

This is a bit concerning that the whole emoji gets deleted, other people would easily be able to tell there are zwjs in the password. I think the problem isn't with "complex" characters, the crash is likely caused by surrogate pairs being broken up and the crash happens because it isn't valid encoding. Maybe try turning CodeUnitBoundary into CodePointBoundary (in retrospect it definitely doesn't make sense to introduce CodeUnitBoundary).
Also when obfuscating the password I think each codepoint should be turned into a *, not each code unit ( I think that's what we are doing right now?)

@flutter-dashboard

Copy link
Copy Markdown

This pull request executed golden file tests, but it has not been updated in a while (20+ days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@goderbauer

Copy link
Copy Markdown
Member

(triage) @justinmc Do you still have plans for this PR?

@justinmc

Copy link
Copy Markdown
Contributor Author

Taking a look to see if I can turn this PR around, I'll make a decision on it by EOD today.

@justinmc

Copy link
Copy Markdown
Contributor Author

I forgot about this PR, but I played around with it today and I think I have a solution. Will put this in draft mode and open it for review soon.

@justinmc
justinmc marked this pull request as draft May 10, 2023 23:58
@justinmc
justinmc marked this pull request as ready for review May 11, 2023 18:57
@justinmc justinmc changed the title Treat complex characters as a single character in obscure mode Fix the breaking of multi-code-unit characters in obscure mode May 11, 2023
@justinmc
justinmc requested a review from LongCatIsLooong May 11, 2023 20:02
@github-actions github-actions Bot added a: internationalization Supporting other languages or locales. (aka i18n) d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos documentation engine flutter/engine related. See also e: labels. f: integration_test The flutter/packages/integration_test plugin platform-ios iOS applications specifically labels May 15, 2023
@justinmc
justinmc removed the request for review from christopherfujino May 16, 2023 18:40
/// * https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF
/// * [isLowSurrogate], which checks the same thing for low (second)
/// surrogates.
static bool isHighSurrogate(int value) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: maybe assert value is in the correct range?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I just tried this on your suggestion, but then realized that there were a million test failures. I think these methods check lots of code units that are not necessarily surrogates.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

sorry I meant valid UTF16 code range 0x0 – 0xFFFFF

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ohh agreed, I'll do that!

return position;
}

return _breaksSurrogatePair(position)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah I see _breaksSurrogatePair also checks for position - 1. It's probably not necessary since the program would have crashed before getting here if the string isn't valid UTF16. That would simplify the logic in this method too.

if (position < 0) {
return 0;
}
if (position == _text.length - 1) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

By defninition, if the last code unit is a low surrogate then getTrailingTextBoundaryAt(_text.length - 1) should return null?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I thought it was right as-is, but let me know if I'm misunderstanding. An example would be the string '👨', a valid surrogate pair, where the last code unit is a low surrogate. If position is in the center of that surrogate pair, then the trailing boundary is at the end of the string, right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This method should return the next start index of a code point AFTER position. There's nothing after the last code unit so null should be returned?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We talked offline and decided that it is possible to return _text.length here. I've updated the docs to explicitly state this in 754a3b0.

);

await tester.tap(find.byType(EditableText));
await tester.enterText(find.byType(EditableText), '👨‍👩‍�');

@LongCatIsLooong LongCatIsLooong May 22, 2023 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could you add a comment to explain what code unit this is? I can't really copy that since it's rendered as 0xFFFD and it's copied as such.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also what happens if there's a sequence of high surrogates or low surrogates?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call. In cacb4fc I've replaced these crazy characters with their \u equivalents and left comments about where they come from. I also added two tests, one with a series of high surrogates and one with a series of low surrogates.

@justinmc justinmc added the autosubmit Merge PR when tree becomes green via auto submit App label May 23, 2023
@auto-submit
auto-submit Bot merged commit 4341ed4 into flutter:master May 23, 2023
@justinmc
justinmc deleted the obscure-emojis branch May 23, 2023 20:22
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 24, 2023
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request May 24, 2023
flutter/flutter@f86c529...216605b

2023-05-24 [email protected] Roll Flutter Engine from aba9dc4640c2 to eebcf36cd38f (3 revisions) (flutter/flutter#127458)
2023-05-24 [email protected] Roll Flutter Engine from 76afb431a740 to aba9dc4640c2 (1 revision) (flutter/flutter#127453)
2023-05-24 [email protected] Roll Flutter Engine from fc6df9512aa2 to 76afb431a740 (3 revisions) (flutter/flutter#127445)
2023-05-23 49699333+dependabot[bot]@users.noreply.github.com Bump actions/labeler from 6b107e7a7ee5e054e0bcce60de5181d21e2f00fb to 2713f7303c96cb1e69627957ec16eea0fd7f94a4 (flutter/flutter#127438)
2023-05-23 [email protected] Roll Flutter Engine from 3535e0d301dd to fc6df9512aa2 (1 revision) (flutter/flutter#127440)
2023-05-23 [email protected] Migrates android semanitcs integration to integration test (flutter/flutter#127128)
2023-05-23 [email protected] Roll Flutter Engine from 23b04314d5d2 to 3535e0d301dd (3 revisions) (flutter/flutter#127428)
2023-05-23 [email protected] Roll Flutter Engine from ccf50f344d5d to 23b04314d5d2 (2 revisions) (flutter/flutter#127421)
2023-05-23 [email protected] Give channel descriptions in `flutter channel`, use branch instead of upstream for channel name (flutter/flutter#126936)
2023-05-23 [email protected] Avoid catching errors in TextPainter tests (flutter/flutter#127307)
2023-05-23 [email protected] [flutter_tools] delete entitlements files after copying to macos build dir (flutter/flutter#127417)
2023-05-23 [email protected] Roll Flutter Engine from a6e37bde65fd to ccf50f344d5d (7 revisions) (flutter/flutter#127416)
2023-05-23 [email protected] modal bottom sheet barrier label (flutter/flutter#126431)
2023-05-23 [email protected] Fix the breaking of multi-code-unit characters in obscure mode (flutter/flutter#123366)
2023-05-23 [email protected] Roll Flutter Engine from 168b0bf3f70d to a6e37bde65fd (1 revision) (flutter/flutter#127397)
2023-05-23 [email protected] Revert "Log all output of ios-deploy" (flutter/flutter#127405)
2023-05-23 [email protected] Roll Packages from 83959fb to d449a17 (6 revisions) (flutter/flutter#127394)
2023-05-23 [email protected] Log all output of ios-deploy (flutter/flutter#127222)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
CaseyHillers pushed a commit to CaseyHillers/flutter that referenced this pull request May 24, 2023
…er#123366)

This PR changes the character boundary behavior of obscured fields to be based on code points instead of code units.

So it used to be possible to traverse and delete obscured text inside of code points (and breaking a code point like that would cause a crash):

![output2](https://github.com/flutter/flutter/assets/389558/674c89a4-c47d-4cdc-a402-4cadb5d2f73b)

But now moving the cursor and deleting is based on code points:

![output1](https://github.com/flutter/flutter/assets/389558/e46301f7-b5af-48d2-812a-0ad649f1383b)

### Native behavior

Native iOS deletes part of the emoji, native Mac deletes the whole emoji.  See flutter#122381 (comment).  So it's unclear what the desired behavior should actually be.

### Resources

Fixes flutter#122381

I thought this might not fix the case where a broken emoji is directly pasted into the field, but it seems to work by trying this:  �������

CC @LongCatIsLooong
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 16, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 17, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 17, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Aug 17, 2023
creatorpiyush pushed a commit to creatorpiyush/packages that referenced this pull request Jun 10, 2026
flutter/flutter@f86c529...216605b

2023-05-24 [email protected] Roll Flutter Engine from aba9dc4640c2 to eebcf36cd38f (3 revisions) (flutter/flutter#127458)
2023-05-24 [email protected] Roll Flutter Engine from 76afb431a740 to aba9dc4640c2 (1 revision) (flutter/flutter#127453)
2023-05-24 [email protected] Roll Flutter Engine from fc6df9512aa2 to 76afb431a740 (3 revisions) (flutter/flutter#127445)
2023-05-23 49699333+dependabot[bot]@users.noreply.github.com Bump actions/labeler from 6b107e7a7ee5e054e0bcce60de5181d21e2f00fb to 2713f7303c96cb1e69627957ec16eea0fd7f94a4 (flutter/flutter#127438)
2023-05-23 [email protected] Roll Flutter Engine from 3535e0d301dd to fc6df9512aa2 (1 revision) (flutter/flutter#127440)
2023-05-23 [email protected] Migrates android semanitcs integration to integration test (flutter/flutter#127128)
2023-05-23 [email protected] Roll Flutter Engine from 23b04314d5d2 to 3535e0d301dd (3 revisions) (flutter/flutter#127428)
2023-05-23 [email protected] Roll Flutter Engine from ccf50f344d5d to 23b04314d5d2 (2 revisions) (flutter/flutter#127421)
2023-05-23 [email protected] Give channel descriptions in `flutter channel`, use branch instead of upstream for channel name (flutter/flutter#126936)
2023-05-23 [email protected] Avoid catching errors in TextPainter tests (flutter/flutter#127307)
2023-05-23 [email protected] [flutter_tools] delete entitlements files after copying to macos build dir (flutter/flutter#127417)
2023-05-23 [email protected] Roll Flutter Engine from a6e37bde65fd to ccf50f344d5d (7 revisions) (flutter/flutter#127416)
2023-05-23 [email protected] modal bottom sheet barrier label (flutter/flutter#126431)
2023-05-23 [email protected] Fix the breaking of multi-code-unit characters in obscure mode (flutter/flutter#123366)
2023-05-23 [email protected] Roll Flutter Engine from 168b0bf3f70d to a6e37bde65fd (1 revision) (flutter/flutter#127397)
2023-05-23 [email protected] Revert "Log all output of ios-deploy" (flutter/flutter#127405)
2023-05-23 [email protected] Roll Packages from 83959fb to d449a17 (6 revisions) (flutter/flutter#127394)
2023-05-23 [email protected] Log all output of ios-deploy (flutter/flutter#127222)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
bisor0627 pushed a commit to bisor0627/packages that referenced this pull request Jun 19, 2026
flutter/flutter@f86c529...216605b

2023-05-24 [email protected] Roll Flutter Engine from aba9dc4640c2 to eebcf36cd38f (3 revisions) (flutter/flutter#127458)
2023-05-24 [email protected] Roll Flutter Engine from 76afb431a740 to aba9dc4640c2 (1 revision) (flutter/flutter#127453)
2023-05-24 [email protected] Roll Flutter Engine from fc6df9512aa2 to 76afb431a740 (3 revisions) (flutter/flutter#127445)
2023-05-23 49699333+dependabot[bot]@users.noreply.github.com Bump actions/labeler from 6b107e7a7ee5e054e0bcce60de5181d21e2f00fb to 2713f7303c96cb1e69627957ec16eea0fd7f94a4 (flutter/flutter#127438)
2023-05-23 [email protected] Roll Flutter Engine from 3535e0d301dd to fc6df9512aa2 (1 revision) (flutter/flutter#127440)
2023-05-23 [email protected] Migrates android semanitcs integration to integration test (flutter/flutter#127128)
2023-05-23 [email protected] Roll Flutter Engine from 23b04314d5d2 to 3535e0d301dd (3 revisions) (flutter/flutter#127428)
2023-05-23 [email protected] Roll Flutter Engine from ccf50f344d5d to 23b04314d5d2 (2 revisions) (flutter/flutter#127421)
2023-05-23 [email protected] Give channel descriptions in `flutter channel`, use branch instead of upstream for channel name (flutter/flutter#126936)
2023-05-23 [email protected] Avoid catching errors in TextPainter tests (flutter/flutter#127307)
2023-05-23 [email protected] [flutter_tools] delete entitlements files after copying to macos build dir (flutter/flutter#127417)
2023-05-23 [email protected] Roll Flutter Engine from a6e37bde65fd to ccf50f344d5d (7 revisions) (flutter/flutter#127416)
2023-05-23 [email protected] modal bottom sheet barrier label (flutter/flutter#126431)
2023-05-23 [email protected] Fix the breaking of multi-code-unit characters in obscure mode (flutter/flutter#123366)
2023-05-23 [email protected] Roll Flutter Engine from 168b0bf3f70d to a6e37bde65fd (1 revision) (flutter/flutter#127397)
2023-05-23 [email protected] Revert "Log all output of ios-deploy" (flutter/flutter#127405)
2023-05-23 [email protected] Roll Packages from aa206a7 to 2afb291 (6 revisions) (flutter/flutter#127394)
2023-05-23 [email protected] Log all output of ios-deploy (flutter/flutter#127222)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: internationalization Supporting other languages or locales. (aka i18n) a: text input Entering text in a text field or keyboard related problems autosubmit Merge PR when tree becomes green via auto submit App d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos engine flutter/engine related. See also e: labels. f: integration_test The flutter/packages/integration_test plugin framework flutter/packages/flutter repository. See also f: labels. platform-ios iOS applications specifically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[macOS][desktop]: Deleting emojis from textfield with obscured text causes crash

4 participants