-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
[lexical] Bug Fix: Fix Chrome on android deletion bugs #7122
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
size-limit report 📦
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks "fine" to me, I think the setTimeout is setting ourselves up for some future clean-up. I don't have an android chrome device to do any testing with so I am not volunteering to come up with anything more elegant. It would be nice if someone with access to this kind of device could verify before merge.
Co-authored-by: Bob Ippolito <bob@redivi.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice if someone with access to an Android + Chrome device could take a look at this before merge to confirm the bug & fix
// When deleting text across multiple paragraphs, Chrome on Android shifts selection rightwards | ||
// This is a workaround to restore the correct selection | ||
if (IS_ANDROID_CHROME) { | ||
setTimeout(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the reason to wrap the workaround in setTimeout with no delay param to:
remove the function from the execution queue and it will only be invoked after JavaScript has finished with the current execution queue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason is that few people understand how lexical event handling works so they add hacks like this. It’s certainly not strictly necessary to use setTimeout here, but I don’t have access to android to trace exactly when/why this code needs to run.
Usually the root cause for hacks like this is that an event comes in from the browser and then lexical handles that event without preventing default but also needs to do something after the platform handles the default. Lexical doesn’t have good/obvious primitives for queuing that up so people write code like this which “works” but has subtle problems of its own.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The update cascade happens because lexical’s reconciliation happens at the next microtask which will be before the setTimeout callback runs, so a second reconciliation will happen because of this workaround. If the code was queued up in precisely the right way only one lexical reconciliation would occur.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@potatowagon Yep you are absolutely right.
Here's the current flow:
- User presses backspace
beforeinput
event is triggered, handling the deletion- Reconciliation happens
- We call
domSelection.setBaseAndExtent()
, shifting the caret to the correct position - The browser emits
selectionchange
, incorrectly shifting the caret
Using setTimeout
allows us to restore the selection after selectionchange
has occurred.
@etrepum If you are against the use of setTimeout
you are welcome to suggest a better fix. Perhaps you can try augmenting the selectionchange
handler to detect this change and revert it there, but I personally don't think that would work better. If its performance you are worried about, I don't think it makes a big difference given that backspacing normally within the same line already requires two reconciliations per character, based on testing on my Pixel.
If you need access to an Android device, I suggest https://www.browserstack.com/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out that this indeed did cause problems, I am guessing because it is over-writing some other changes to the selection that are expected to happen due to a different update or event. For now it will be reverted (#7218) because it causes worse problems in certain situations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. Turns out that subsequent browser changes to selection are sometimes desired. Apologies to all those affected by the bugs introduced.
Screen_Recording_20250206-153236_Samsung.Internet.mp4im unable to repro the issue on my android, could it be a keyboard setting? not sure if i need to enable maybe Right to Left language in my android chrome browser? |
testing changes from https://lexical-playground-my5rjurxj-fbopensource.vercel.app/ Screen_Recording_20250206-154331_Telegram_1.mp4i dont see any issues so should be safe to merge, tho i havnt yet been able to repro the bug |
Looks like you are using the Samsung keyboard, which doesn't seem to have this issue. Try using GBoard? It's the default browser on my Pixel device. |
thanks for this info. i have access to a google pixel too so let me try it there |
screen-20250207-004517.mp4still unable to repro the bug on GBoard on a google pixel. Gboard About shown towards end of screen capture, has version info. could it be a particular keyboard setting? like language? tested the changes in this PR on the google pixel and no issues either |
The bug occurs when deleting across lines: video_2025-02-07_01-39-17.mp4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a "Real" Google Pixel device on a Sauce Labs trial I was able to reproduce the bug and confirm that this PR fixes it. The issue with setTimeout
is less about performance and more about how complicated things get when these events are not happening in the order you expect them to which often leads to different bugs. I think this is fine for now, it clearly fixes an issue and probably doesn't yet cause other ones, but should be cleaned up in a more systematic way that doesn't require the setTimeout later. I don't have a specific suggestion for exactly what the best fix is without doing a much deeper dive into this part of the code while having a live android device accessible for debugging.
I just tested this change on my current setup, where I have |
Please file a new detailed issue with a full repro if you’d like anyone to look at this bug, comments on closed PRs will get lost. |
Description
Fixes #4340, #5042 and #1965
DELETE_CHARACTER_COMMAND
, however the browser is unable to deleted decorator nodes, this PR detects this scenario and dispatchesDELETE_CHARACTER_COMMAND
accordinglyTest plan
Before
screen-20250203-030555.mp4
After
screen-20250203-030819.mp4