Skip to content

[lexical] Bug Fix: Ignore beforeinput and input events in captured decorators#8740

Merged
etrepum merged 5 commits into
facebook:mainfrom
etrepum:firefox-152-beforeinput
Jun 25, 2026
Merged

[lexical] Bug Fix: Ignore beforeinput and input events in captured decorators#8740
etrepum merged 5 commits into
facebook:mainfrom
etrepum:firefox-152-beforeinput

Conversation

@etrepum

@etrepum etrepum commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Description

Firefox 152 changed how it dispatches beforeinput events which caused lexical to process text input when it should be captured in the decorator's native input (this also applies to controls added via DOMSlot, which work very similarly).

onBeforeInput

  • Adds the event context to the update for BEFORE_INPUT_COMMAND dispatch, but only dispatches when input is not captured

onInput

  • Now only dispatches when input is not captured (it already had the event context)

isSelectionWithinEditor

  • Only silences exceptions around the DOM calls, invalid code can fail now
  • Reads $isSelectionCapturedInDecoratorInput with editor context (would fail silently before)

$isSelectionCapturedInDecoratorInput

  • Renamed from isSelectionCapturedInDecoratorInput (it requires an active editor, wasn't named correctly before)
  • Treats any activeElement contained in a LexicalNode containing an input, textarea, or non-LexicalEditor contenteditable as

$markSlotEditable

  • Any contentEditable="true" (can be named slots or the children DOMSlot) now contains a __lexicalEditor property like the RootElement to let event handling know that their contents should be handled by lexical

Closes #8738

Test plan

New browser unit test to simulate this condition, no version of playwright has Firefox 152 yet so we can't do a meaningful e2e test

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 23, 2026
@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lexical Ready Ready Preview Jun 24, 2026 5:25am
lexical-playground Ready Ready Preview Jun 24, 2026 5:25am

Request Review

@potatowagon potatowagon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed by Navi (Tater Thoughts Bobblehead) on behalf of @potatowagon.

LGTM ✅ — Bug fix: Ignore beforeinput and input events in captured decorators.

What this does: Extracts the "is this input from a captured decorator?" check out of $handleInput and into a shared shouldIgnoreInputFromCapturedSelection helper. This helper is now called in both onBeforeInput (early-return before dispatchCommand(BEFORE_INPUT_COMMAND, ...)) and onInput (wrapping the updateEditorSync call). Previously, only the input event was filtered at the command handler level — beforeinput was never filtered, meaning decorator inputs (e.g. <input> elements inside DecoratorNodes) would still trigger BEFORE_INPUT_COMMAND and potentially corrupt editor state.

What I checked:

  1. Logic correctness: The helper checks two paths — (a) composed event target is directly inside a capturing decorator, and (b) the active element (via getActiveElementDeep for shadow DOM) is a capturing decorator. Both are existing guards, just factored out and applied earlier.
  2. Behavioral change: Moving the guard from $handleInput (after updateEditorSync entry) to onInput (before updateEditorSync) means no editor update cycle is started at all for captured decorator inputs. This is strictly better — avoids unnecessary commit overhead.
  3. beforeinput coverage: The new early-return in onBeforeInput fills a real gap. Without it, Firefox in particular fires beforeinput that bubbles to the contentEditable root even when focus is on an inner <input>. This could trigger BEFORE_INPUT_COMMAND handlers that assume a Lexical selection context.
  4. Test quality: 108-line browser test simulates exactly the Firefox scenario — dispatches untrusted beforeinput/input from a focused decorator <input>, then document.execCommand. Verifies neither BEFORE_INPUT_COMMAND nor INPUT_COMMAND fire, and the native input still works (input.value === "a").
  5. www compat: No removed/renamed exports. No API surface changes. The fix only suppresses spurious events — www consumers of BEFORE_INPUT_COMMAND/INPUT_COMMAND will simply stop receiving events they should never have received.
  6. Edge cases: Shadow DOM path handled via getActiveElementDeep. Null checks on rootElement/activeElement present. rootElement.contains(activeElement) ensures only elements within this editor are checked.

CI: Core unit (22.x + 24.x), browser (all platforms), integrity, integration, e2e plain-text (linux + windows) — all GREEN. Remaining e2e (collab, mac, rich-text) still pending.

Ready to approve once remaining e2e completes.

@levensta

Copy link
Copy Markdown
Contributor

If events from decorators are supposed to be ignored, why are key shortcuts propagates on to the editor?

Screen.Recording.2026-06-23.at.23.25.24.mov
@etrepum

etrepum commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator Author

This ignores them when they are captured in a native input, textarea, or some other active element that isn't a lexical editor.

@etrepum

etrepum commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator Author

This change affects beforeinput & input events and how selection is mapped, keyboard shortcuts are handled by keydown events which doesn't have this check

…ontrols

Adds the event-layer guard for facebook#8738 on top of the selection-layer changes
already on this branch.

Firefox 152 changed how it dispatches beforeinput/input for native controls
(such as an <input> inside a decorator): the event is retargeted to the editor
root rather than the focused control, so its (composed) target no longer
identifies the control. Lexical then processed the keystroke as editor input and
inserted text outside the component, with a stationary caret producing reversed
characters. The Firefox 152 behavior can only be reproduced on a real
Firefox >= 152 build (Playwright cannot run it) and was verified manually.

Add isInputEventTargetingCapturedSelection(), which detects that a
beforeinput/input belongs to a captured decorator subtree by checking both the
composed event target and the deep active element. The active-element signal is
what survives Firefox 152's retargeting. onBeforeInput and onInput still run
their updateEditorSync({event}) so the editor selection stays in sync with the
DOM; they only skip dispatching BEFORE_INPUT_COMMAND / INPUT_COMMAND when the
guard matches. This generalizes and replaces the narrower composed-target-only
check that previously lived in $handleInput, and extends the same protection to
beforeinput.

Test (browser-mode, real layout/selection engine): LexicalFirefoxDecoratorRetarget
covers both the event dispatched from the focused control and the event
retargeted to the editor root; it asserts at CRITICAL priority that no input
command is dispatched, and both cases fail without this guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G6SoWfs6U6w2CK8BXQn8WS
@etrepum etrepum added this pull request to the merge queue Jun 25, 2026
Merged via the queue into facebook:main with commit 225a223 Jun 25, 2026
45 checks passed
@etrepum etrepum mentioned this pull request Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. extended-tests Run extended e2e tests on a PR

4 participants