Skip to content
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

Selection breaks while composition in iPad Safari + ShadowDOM #1498

Closed
Siubaak opened this issue Nov 22, 2024 · 6 comments
Closed

Selection breaks while composition in iPad Safari + ShadowDOM #1498

Siubaak opened this issue Nov 22, 2024 · 6 comments

Comments

@Siubaak
Copy link

Siubaak commented Nov 22, 2024

Issue

Minimal example: https://siubaak.github.io/siudnote/pmbug
Minimal code: https://github.com/Siubaak/siubaak.github.io/blob/master/siudnote/pmbug.html

I've built a ProseMirror editor mounted at a shadow dom. It works great in all platforms except iPad Safari. When composing a Chinese input, selection will automatically wrap the whole input in iPad Safari.

And I digged a litte bit into prosemirror-view. I found that, when I'm inputing, flush will be called due to composition events, dom mutations, selection changed or whatever. It doesn't matter. Then, inside flush, it will get current selection by view.domSelectionRange(). Because of the true condition in this scenario by checking safari && this.root.nodeType === 11, safariShadowSelectionRange will be called eventually. Inside safariShadowSelectionRange, selection.getComposedRanges will be called since we're right now inside a shadow root in Safari, and rangeToSelectionRange will also be called for transforming the result of selection.getComposedRanges to a valid selection range.

Here's the point. I think isEquivalentPosition inside rangeToSelectionRange is where the problem occurs in. Let me explain further.
image

Reproducing

At the beginning, I press n, then isEquivalentPosition will be called with parameters <section>n</section>, 0, #text n, 1. The result will be false. Fair enough.
image

Then, I press h and something seems to be wrong. isEquivalentPosition will be called with parameters <section>n h</section>, 1, #text n h, 3. The result will be true, so the anchor and the focus will get swapped from [0, 3] to [3, 0], which next will make the selection wrap the whole input.
image

After that, no matter what I input, isEquivalentPosition will be called with parameters <section>anything</section>, 1, #text anything, n. The result will remain true constantly, then the anchor and the focus will get swapped from [0, n] to [n, 0]. The selection will keep wrapping what I input in this composition. Even though compositionend occurs, this selection still wrap the whole input.
image

What I tried

Since if I don't mount the editor inside a shadow dom, the editor works totally fine. I think it is because of the mistaken swap, so I tried to comment out the isEquivalentPosition clause in rangeToSelectionRange, to prevent the swap. Everything works fine again while composition. I don't think it's a good idea to comment it out, though. So I decided to report this issue here.

Anyway, ProseMirror is a wonderful lib to build a editor. I really appreciate what you have done. :)

@Siubaak
Copy link
Author

Siubaak commented Nov 22, 2024

Found a similar issue. #1455

@Siubaak
Copy link
Author

Siubaak commented Nov 22, 2024

In order to flip back the selection, I use this for workaround.

appendTransaction(transactions, prevState, state) {
  let shouldUpdateSel = false;
  for (const tr of transactions) {
    if (tr.getMeta('composition')) {
      shouldUpdateSel = true;
      break;
    }
  }
  if (shouldUpdateSel) {
    const { tr, doc, selection } = state;
    const { from, to } = selection;
    return tr.setSelection(TextSelection.create(doc, from, to));
  }
}

@marijnh
Copy link
Member

marijnh commented Nov 22, 2024

hen, I press h and something seems to be wrong. isEquivalentPosition will be called with parameters <section>n h</section>, 1, #text n h, 3. The result will be true,

And that is correct—the position after the text node and the position at the end of the text node are the same cursor position. Do you think the position reported by the browser doesn't match the selection at this point?

@Siubaak
Copy link
Author

Siubaak commented Nov 23, 2024

No, I don't think what browser reports is wrong. I think maybe the flip when the positions are equivalent doesn't work properly for composition?

I compared the behaviour between the non-ShadowDOM scenario and the ShadowDOM scenario. Following the same reproducible steps, I printed view.domSelectionRange() out, then I found:

For non-ShadowDOM scenario:
image

For ShadowDOM scenario:
image

In the non-ShadowDOM scenario, when I pressed h, the browser native position was [0, 3], and then ProseMirror soon modified it to be [3, 3] in flush. That works fine.

But in the ShadowDOM scenario, when I pressed h, the browser native position was [0, 3], but the flip made the position to be [3, 0], which led to a wrong selection I think?

marijnh added a commit to ProseMirror/prosemirror-view that referenced this issue Nov 23, 2024
FIX: Fix an issue on Safari where the (incorrect) top-level selection was used, instead
of the workaround that also works inside shadow roots.

Issue ProseMirror/prosemirror#1455
Issue ProseMirror/prosemirror#1498
@marijnh
Copy link
Member

marijnh commented Nov 23, 2024

I think I found the issue. I don't think it really was at the place you point at, though indeed, turning off that check accidentally makes it behave a bit better. Attached patch should be more robust though. Could you give it a try?

@Siubaak
Copy link
Author

Siubaak commented Nov 23, 2024

Oh yeah. It looks great. I can see more ajustments right after my input, which corrects the final selection.

image

Thank you so much. Let's close this issue. :D

@Siubaak Siubaak closed this as completed Nov 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants