Skip to content

add editableTextId - a stable unique TextEditable identifier#79683

Closed
yjbanov wants to merge 2 commits into
flutter:masterfrom
yjbanov:editable-text-id
Closed

add editableTextId - a stable unique TextEditable identifier#79683
yjbanov wants to merge 2 commits into
flutter:masterfrom
yjbanov:editable-text-id

Conversation

@yjbanov

@yjbanov yjbanov commented Apr 2, 2021

Copy link
Copy Markdown
Contributor

Attach an editableTextId value to the EditableTextState that uniquely identifies the text field and pass it to the engine via text input channel and semantics nodes. This field will be used to fix a race between the flutter/textinput channel and semantics.

This makes progress towards fixing #52106 (engine changes are required to fully fix this).

Background

On the web, the semantics tree is responsible for hosting the <input> elements. The semantics update happens after the TextInputConnection is established. Conversely, the semantics tree can create <input> elements before text input connection is established. This leads to the following bad situations leading to various bugs:

  • A text input command is sent to the engine, but there's no <input> element to sync to.
  • Text input command is routed to a wrong <input> element.
  • <input> is removed (due to lazy rendering) while a text input connection is still alive.

Solution

editableTextId outlives clientId (which is unique to a single TextInputConfiguration), and it is the single source of truth for both semantics and the text input channel. Whether one or the other initiates the creation of an <input> element, they can connect by matching on the editableTextId.

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, including Features we expect every widget to implement.
  • I signed the CLA.
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt.
  • All existing and new tests are passing.

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

@flutter-dashboard flutter-dashboard Bot added a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) framework flutter/packages/flutter repository. See also f: labels. labels Apr 2, 2021
@google-cla google-cla Bot added the cla: yes label Apr 2, 2021
}
}

int get editableTextId => _editableTextId;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

here and elsewhere: missing docs

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.

Yeah, lots of clean up to do, lots of docs to write. For now just sending a draft. Also scheduled a VC for this Thursday to discuss if we're OK with this approach overall.

this.tags,
this.transform,
this.customSemanticsActionIds,
required this.editableTextId,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this only be set if the flags indicate that it is a textfield? Maybe assert?

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.

Unfortunately, const asserts don't support the expression flag & SemanticsFlag.isTextField.value == 0. If you can think of a workaround, I'm all ears. FWIW, I'll have this assertion in dart:ui.

assert(value != null);
if (value != _editableTextId) {
_editableTextId = value;
markNeedsPaint();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why does this require a paint update (seems to be only used in describeSemanticsConfiguration)?

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.

Done. For some reason I thought a semantics change always implied a repaint, but I guess that doesn't have to be the case.

/// configurations as determined by [isCompatibleWith].
void absorb(SemanticsConfiguration child) {
assert(!explicitChildNodes);
assert(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this condition already ensured by isCompatibleWith? isCompatibleWith should probably mark configurations that have different _editableTextIds as incompatible?

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.

Done. I would also like to assert that no two editables are nested. Is there a good place for such an assertion?

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.

Although, I suppose a single text field can be described by two nodes each supplying a different subset of semantic properties, so maybe we should allow it.

@goderbauer

Copy link
Copy Markdown
Member

editableTextId outlives clientId (which is unique to a single TextInputConfiguration)

Instead of inventing a new ID could we just make clientID live longer and use that?

@yjbanov

yjbanov commented Apr 13, 2021

Copy link
Copy Markdown
Contributor Author

editableTextId outlives clientId (which is unique to a single TextInputConfiguration)

Instead of inventing a new ID could we just make clientID live longer and use that?

If we are sure that TextInputConnection never needs a unique ID, then I can give it a shot. It may be a breaking change for platform implementors/embedders due to the expectation that once you send TextInput.clearClient its clientID is no longer valid and all resources associated with it can be freed. It would also be mildly breaking on the framework side as the client ID would have to move from the hidden TextInputConnection._id field to, say, a public TextInputClient.id field. Other than that, I don't see why not.

@yjbanov

yjbanov commented Apr 28, 2021

Copy link
Copy Markdown
Contributor Author

Closing in favor of an approach that doesn't require framework-side changes (flutter-team-archive/engine#25797)

@yjbanov yjbanov closed this Apr 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants