[lexical] Fix: exclude Android WebView from IS_SAFARI browser detection#8267
Conversation
Android WebView's user agent string contains "Version/4.0 ... Safari/537.36", which causes the IS_SAFARI regex to match. This makes Lexical think it's running in Safari when it's actually in an Android WebView. When IS_SAFARI is incorrectly true on Android, three things break: 1. Ghost characters after deleting text — COMPOSITION_SUFFIX uses a non-breaking space (Safari path) instead of a zero-width space (Android path). The non-breaking space persists visibly after the user deletes all text. 2. Broken backspace — the Android-specific composition cleanup in $updateTextNodeFromDOMContent is guarded by !IS_SAFARI and gets skipped, so empty text nodes aren't cleaned up after composition ends. 3. Wrong composition-end handling — the Safari-specific isSafariEndingComposition workaround activates, deferring keydown handling in a way that swallows keystrokes on Android. Fix: move IS_ANDROID before IS_SAFARI and add && !IS_ANDROID to exclude Android WebView from the Safari detection.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Hi @kzroo! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks! |
Bug
Android WebView's user agent string contains
Version/4.0 ... Safari/537.36, which causes theIS_SAFARIregex inpackages/shared/src/environment.tsto match. This means Lexical thinks it's running in Safari when it's actually running in an Android WebView.This affects any app embedding Lexical inside an Android WebView — including apps built with Capacitor, Cordova, React Native WebView, or any custom WebView wrapper.
What goes wrong
When
IS_SAFARIis incorrectlytrueon Android, three things break:Ghost characters after deleting text:
COMPOSITION_SUFFIXuses a non-breaking space (\u00A0, the Safari path) instead of a zero-width space (\u200b, the Android path). The non-breaking space is visible and persists in the DOM after the user deletes all text, leaving a "ghost" character that can't be removed.Broken backspace behavior: The Android-specific composition cleanup code in
$updateTextNodeFromDOMContentis guarded byif (!IS_SAFARI && !IS_IOS && !IS_APPLE_WEBKIT)— so it gets skipped entirely. This means empty text nodes aren't cleaned up properly after composition ends, causing erratic cursor positioning and broken backspace.Wrong composition-end handling: The Safari-specific
isSafariEndingCompositionworkaround activates, which defers keydown handling to prevent an extra character deletion in Safari. On Android this deferral causes the opposite problem — keystrokes are swallowed or applied to the wrong position.The editor works perfectly in Chrome browser on Android (where the UA does not contain
Version/). The bug only manifests inside WebViews.Fix
Move
IS_ANDROIDbeforeIS_SAFARIinpackages/shared/src/environment.tsand add&& !IS_ANDROIDto exclude Android WebView from the Safari detection.Test plan