fix(html): record what an Android keyboard types into a document - #905
Merged
Merged
Conversation
An Android keyboard holds a composition open on the word under the caret for as long as the caret stays there. The document editor lost that input in two ways: - On `compositionend` it compared a run with itself, so the step was always empty and was dropped. - While a composition was open, it let every `beforeinput` through without a record, including the keys, Enter and Backspace that it can cancel. So scope `paragraph` did not hold there either. The page showed the text, but the log was empty, and a save wrote the document as it was. Now the editor notes a run's text before the browser writes into it, and records the change after the `input`. Only an event that cannot be cancelled goes through to the browser. Before the editor acts, and before an undo or a redo, it records what the browser wrote. Checked with the browser checks and on an Android 12 emulator with Gboard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F6AQY2k86AaPq12nxBfN7A
# Conflicts: # CHANGELOG.md # src/odr/internal/html/frontend/document.js
The `beforeinput` handler started a gesture and then recorded what the browser wrote without an `input`, so that text and the key after it had one gesture, and one undo took back both. The handler now records the text first. WebKit can fire a composition `beforeinput` that can be cancelled. The editor refused it, so the browser typed nothing. The editor now lets every composition type through. `committed()` no longer drops text that the browser wrote but the log does not hold yet, because the save did not contain that text. The comments, the design notes and the changelog entry are shorter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C725vurM2ZqBY6ddV2FMZ7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An Android keyboard (Gboard) holds a composition open on the word under the caret for as long as the caret stays there. The document editor (
document.js, the odt, docx and pptx view) lost that input in two ways:compositionendit calledsetRunText(target, target.textContent).setRunTextreadsbeforefrom the same run, so the two texts were always equal, and the step was dropped as a no-op.composing !== null, it let everybeforeinputthrough without a record, includinginsertText,insertParagraphand the deletes, which it can cancel. The browser applied them natively. This is also why Enter split a paragraph under scopeparagraph.The page showed the typed text, but
getOperations()stayed empty, and a save wrote the document as it was. Found while integrating 7.0.0 in OpenDocument.droid (opendocument-app/OpenDocument.droid#662).The fix
compositionstart, and on eachbeforeinputthat cannot be cancelled). After theinput, it records the change as one step.record()puts the step on the log without applying it, because the page shows it already. Rewriting the text node would break the composition.formatortoggle, it records what the browser wrote. This keeps the log in order.compositionendstill reads the watched runs back, for a browser that writes a composition without aninputfor it.Checked
test/browser/text/tests.html: a new group, "a composition", has 21 checks. All 190 checks pass in Chrome. Againstmain, the group's first check fails, and the next one throws.insertCompositionTextfrom tapping the on-screen keys) is recorded key by key.adb input text, the cancelableinsertTextper character is taken by the editor.document, and is refused asoutOfScopeunder scopeparagraph.Seen, not fixed here
insertParagraphat offset 6 in"TextHi "while the selection is at 7, because it does not render the trailing space. So the space moves into the new paragraph. This has nothing to do with compositions, and a desktop Chrome probably does the same.The editor's inline copy in the reference outputs changes, so the pins probably need a follow-up advance, as after #897.