Case ce-0566-safari-table-cell-composition-leak · Scenario scenario-table-composition-leaks

IME composition text moves outside empty table cell

OS: macOS 14.4 Device: Desktop Any Browser: Safari 17.4 Keyboard: Japanese IME Status: confirmed
ime composition table safari-17 selection-leak

Phenomenon

Safari’s WebKit engine has a flaw in how it resolves “Logical to Physical” selection mapping within table structures. When a user initiates and commits an IME composition (common in CJK languages) inside a <td> cell that contains no other text or nodes, the final insertion point is incorrectly calculated as being outside the cell.

Reproduction Steps

  1. Render a structure with an empty table cell: <table><tr><td></td></tr></table>.
  2. Set contenteditable="true" on the cell or its container.
  3. Use a CJK IME (e.g., Japanese Kana, Chinese Pinyin).
  4. Click inside the cell and type enough to start a composition (e.g., type “a”).
  5. Confirm the selection by pressing Enter.

Observed Behavior

  1. compositionstart and compositionupdate: These fire correctly inside the <td>.
  2. compositionend: Fires normally.
  3. DOM Mutation: After composition ends, the character is removed from the temporary composition state but is re-inserted as static text outside the <td>.
  4. Caret Position: The caret often jumps to the very beginning of the document or the start of the table block.

Expected Behavior

The browser should ensure that the Selection range remaines anchored to the parent <td> element throughout the entire composition lifecycle.

Impact

  • Data Integrity: Text appears in the wrong logical order or outside the intended container.
  • Visual Corruption: The table remains empty while the text floats above or below it, breaking the document’s visual layout.
  • Undo History: Since the insertion happened in a “leaked” position, the undo operation might fail to remove the text correctly.

Browser Comparison

  • Safari 17/18: Exhibit the bug.
  • Chrome/Firefox: Correct behavior; the range remains scoped to the <td>.

References & Solutions

Mitigation: Placeholder Node

A known workaround used in ProseMirror is to ensure the cell is never “truly empty” during selection or to manually reset the selection inside the cell upon compositionend.

// Force an invisible placeholder or check range on compositionend
element.addEventListener('compositionend', (e) => {
  const sel = window.getSelection();
  const range = sel.getRangeAt(0);
  
  if (!container.contains(range.commonAncestorContainer)) {
    console.warn('Safari Selection Leak Detected! Fixing...');
    // Manually insert data at the correct location
    insertAtLastKnownValidPath(e.data);
  }
});
Step 1: Empty Cell Focus
|
The caret is placed inside an empty <td>.
Step 2: Composition Start
|
Composition begins. 'あ' appears as an inline composition session inside the cell.
vs
✅ Expected
あ|
Expected: The committed text should remain within the <td> where it was composed.

Browser compatibility matrix

This matrix shows which browser and OS combinations have documented cases for this scenario. The current case is highlighted. Click on a cell to view other cases.

Current case
Confirmed
Draft
No case documented

All variants (detailed table)

Complete list of all cases for this scenario with full environment details.

Case OS Device Browser Keyboard Status
ce-0566-safari-table-cell-composition-leak macOS 14.4 Desktop Any Safari 17.4 Japanese IME confirmed
ce-0575-prosemirror-safari-empty-table-leak macOS 15.2 Desktop Any Safari 18.2 Japanese IME confirmed

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: macOS 14.4
Device: Desktop Any
Browser: Safari 17.4
Keyboard: Japanese IME
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.