#1005·obscura

Backspace splits surrogate pairs (deletes one UTF-16 unit), leaving a lone surrogate on emoji/supplementary chars

Author: mnazaCreated Sep 17, 2026Updated Sep 17, 2026

Summary

Backspace in a text field deletes a single UTF-16 code unit, so pressing it after an emoji or any supplementary character splits the surrogate pair and leaves a lone surrogate, instead of deleting the whole character as browsers do.

Details

crates/obscura-cdp/src/domains/input.rs (BACKSPACE_JS):

  • collapsed caret: v.slice(0, s - 1) + v.slice(s)
  • legacy (selectionStart == null): v.slice(0, -1)

For "a\\\u{1F600}b" (JS length 4) with the caret at position 3, v.slice(0, 2) keeps "a" + the high surrogate and v.slice(3) is "b", producing "a\uD83Db" — a lone surrogate. Chrome deletes the whole emoji, yielding "ab".

Fix

Before deleting at a collapsed caret (or the trailing character on the legacy path), check whether the two code units ending at that position form a surrogate pair (charCodeAt(p-1) in 0xDC00–0xDFFF and charCodeAt(p-2) in 0xD800–0xDBFF); if so, remove 2 code units. The range-delete path (non-empty selection) is already correct.

Affected

  • crates/obscura-cdp/src/domains/input.rsBACKSPACE_JS