Selection/split/join misbehavior with Android Chrome and lists

I’ve been uncovering this rather convoluted mess with lists on Android Chrome. The current situation I have I’ve documented here with screen recordings:

But since I think this relates to ProseMirror proper, I’ll post it here as well.

So basically what I believe happens is, that on Android Chrome if you split/join/delete a list, the selection can move unexpectedly to eg end of list item node above. Which wouldn’t be that terrible but it seems depending on the order ProseMirror moves the cursor, weird things can happen.

Biggest problems I have is with NodeViews but since it’s debatable whether it’s my own fault for not handling the edge cases, I’ve found a similar inconsistency without them:

  1. Open Bug with Android Chrome

  2. Move cursor to start of two

  3. Press enter

  4. Press backspace

  5. Cursor is now between t and wo

At https://prosemirror.net/ I can observe the same behavior as well where the cursor jumps around, moving between t and wo but due to some reason it’s correctly moved back to start of two. It seems the fact that the cursor jumps so much in Android Chrome means that there whole thing should be prevented in the first place. At least from user perspective this looks kinda awkward.

Gboard 14.6.03 arm64

Chrome 129.0.6668.81

Android 13

I notice your reproduction script doesn’t bind any command to backspace. That’s generally unwise, since it means you get the raw browser backspace behavior. Does adding the base keymap help?

I see. Well with Backspace from baseKeymap it works better but still produces the same twitchy behavior as in prosemirror.net.

The big issue I have is with NodeViews since apparently I can’t figure out how to wrap the paragraphs in nodeviews without a random part of list_item being left behind at times.

There was something similar to I asked about before Moving selection with arrows behaves weirdly with NodeView where selection moved weirdly in desktop Chrome with NodeViews. That I, however, managed to fix by using an extra div under the contenteditable false div. This time, no such luck. The selection works as it should on desktop but the splitting on Android still creates extra stuff.

So I just started to debug this again. Here’s how it looks like on my phone

It seems ProseMirror behaves here correctly, within deleteBarrier it calls tr.join which deletes the list_item before the paragraph.

However, apparently there’s a native Chrome event that passes through before all of this can happen, causing the twitching. I tried looking into different APIs but couldn’t find any handlers to prevent this. Can you @marijn point me to a right direction or is preventing these native events impossible?

This goes back to the weird behavior of mobile Chrome, which doesn’t fire backspace key events, so ProseMirror lets the default behavior go through and figures out that a backspace happened from that, correcting the DOM state after the fact.

And if you’re wondering if we cannot just use beforeinput events with inputType == "deleteContentBackward" to detect backspace—unfortunately those are also (ridiculously) fired in a bunch of other situations, such as IME composition updates, and treating them as backspace breaks a number of things.

1 Like

Wow. It really seems that there are some hard corners with content-editable. No wonder Google Docs switched to its own rendering engine.