Case ce-0586-firefox-android-nested-gettargetranges-parent-range · Scenario scenario-gettargetranges-nested-wrong-scope

Firefox Android may report parent contenteditable range in beforeinput for nested inner editor

OS: Android 13+ Device: Phone Any Browser: Firefox 120+ Keyboard: Gboard / stock Status: draft
firefox android fenix getTargetRanges nested beforeinput

Phenomenon

Firefox for Android (Fenix) has community reports that when a contenteditable element is nested inside another contenteditable, beforeinput’s getTargetRanges() sometimes returns a StaticRange anchored in the outer host instead of the inner focused editor. The failure is subtle because the keyboard still drives edits in the inner surface from the user’s perspective, but JavaScript that trusts targetRanges[0] for preventDefault() + manual DOM updates may run against the outer tree—merging lines, deleting outer paragraphs, or inserting adjacent to outer nodes.

Reproduction Steps

  1. Build HTML: outer div contenteditable="true" containing static text and an inner div contenteditable="true".
  2. Open the page in Firefox for Android.
  3. Tap inside the inner editor and place the caret at the end of “Inner”.
  4. Attach a beforeinput logger on document or the outer element that prints getTargetRanges()[0]’s startContainer chain and compares to document.activeElement.
  5. Type a character or press Backspace.
  6. On affected versions, compare logs: activeElement may be the inner host while startContainer’s closest('[contenteditable]') resolves to the outer host.

(Exact builds vary; treat as a regression-sensitive area and re-test after Fenix / Gecko updates.)

Observed Behavior

  • Non-empty but wrong scope: Unlike empty targetRanges, the array has entries—so code paths that only fall back when length === 0 never run.
  • Custom editors break: ProseMirror, Lexical, or hand-rolled beforeinput filters that unwrap StaticRange into Range and replace contents corrupt the outer document.
  • Selection vs targetRanges mismatch: window.getSelection() may still point inside the inner host while targetRanges does not; naive “prefer targetRanges” policies fail.

Expected Behavior

Per Input Events intent, getTargetRanges() for an edit operation scoped to the inner host should use boundary nodes inside that host (or its descendants), consistent with the focused contenteditable that will receive default actions.

Impact

  • Data loss in outer regions: Unintended deletion of outer paragraphs when the user meant to edit the inner caption.
  • Editor SDK bugs: Frameworks assume targetRanges is authoritative when non-empty; mobile Firefox violates that assumption for nesting.

Browser Comparison

  • Firefox Android: Reports of wrong-scope targetRanges for nesting; verify on current release.
  • Chrome Android / Safari iOS: Test separately; different engines, different nesting bugs.
  • Desktop Firefox: Often behaves; do not extrapolate from desktop passes.

Solutions

  1. Containment check:
function rangeInsideHost(range, host) {
  return host && range && host.contains(range.startContainer) && host.contains(range.endContainer);
}

editorRoot.addEventListener('beforeinput', (e) => {
  const host = document.activeElement?.closest?.('[contenteditable="true"]');
  const tr = e.getTargetRanges?.() ?? [];
  if (tr.length && host && !rangeInsideHost(tr[0], host)) {
    e.preventDefault(); // optional: block bad default
    // Fall back to Selection-based logic scoped to `host` only
  }
});
  1. Refuse nested contenteditable in product if Fenix must be supported without complex guards.

  2. Track Fenix #27569 and Gecko tickets for resolution status.

References

Step 1: Nested editables

Outer text

Inner
Outer host wraps an inner contenteditable paragraph.
Step 2: Focus inner, press a key

Outer text

Inner|
Caret inside inner; user types or deletes.
vs
✅ Expected

Outer text

Innerx
Expected: targetRanges are contained within the inner host that owns focus, matching getSelection().

Playground for this case

Use the reported environment as a reference and record what happens in your environment while interacting with the editable area.

Reported environment
OS: Android 13+
Device: Phone Any
Browser: Firefox 120+
Keyboard: Gboard / stock
Your environment
Sample HTML:
Event log
Use this log together with the case description when filing or updating an issue.
0 events
Interact with the editable area to see events here.

Comments & Discussion

Have questions, suggestions, or want to share your experience? Join the discussion below.