Skip to content

v0.0.4 — Backspace works

Choose a tag to compare

@aturzone aturzone released this 04 Sep 09:28
· 29 commits to main since this release

Backspace works. Verified in a real Firefox 155, with real key events, against a real note.

It was one line

The note's shadow host was attached to document.documentElement — a sibling of <body>,
outside it — on the reasoning that a child of <html> is the hardest place for a page to
disturb.

Gecko will not perform an editing command for an editing host that sits outside <body>.
It dispatches beforeinput with deleteContentBackward, does not cancel it, and then declines
to edit: no input event, no change, no error. Text insertion takes a different path and kept
working — which is exactly why the symptom was so lopsided: a note you could write in and could
not erase in.

Measured across four hosts differing only in position and tag name:

host Backspace
<div> inside <body> deletes
custom tag inside <body> deletes
<div> on <html> nothing
custom tag on <html> nothing

The tag name is irrelevant. The position is everything.

Three wrong fixes before this one

Worth saying plainly. Every instrument I had was wrong for the job:

  • The browser I can automate is Chromium-based, and Blink has no such restriction — the bug is
    invisible there.
  • Injected key events carry no physical code, so the browser delivers them as events and
    performs no editing action. Under a synthetic Backspace every editable element on earth
    looks broken. My first "reproduction" proved nothing, and the fix that followed was
    addressing an artefact of my own test.

Two real bugs were found on the way and remain fixed — a caret that was destroyed and never
replaced, and RTL applying to the editor but not the rendered view. Neither was this one.

0.0.3's change is reverted: it removed keyboard events from the note's event containment on
a theory that measurement has now disproved. Containment is back, because it stops a site's
bare / or j/k shortcut firing while you type in a note.

What found it

Atur suggested driving a real browser, which was the unblock. geckodriver now drives the
installed Firefox, and four scripts in spikes/ narrowed it down: the structure (seven
variants, all innocent), the real note instrumented, one intervention at a time, and finally
host position. They are committed — reach for them before theorising about engine behaviour.

Also eliminated, and innocent: the closed shadow root, all: initial, pointer-events: none,
contain: style, plaintext-only vs true, ancestor transforms, the adopted stylesheet, and
four levels of nesting.

Install

  1. Download chevalet_note-0.0.4.zip
  2. about:debugging#/runtime/this-firefox → Load Temporary Add-on… → pick the .zip
  3. Click the toolbar icon and grant access to a site
  4. Alt + double-click on empty page space, or right-click → Add a note here

Unsigned, so it loads temporarily and goes when you close Firefox.

Everything else from 0.0.2 and 0.0.3

Full undo/redo (Ctrl+Z / Ctrl+Y / Ctrl+Shift+Z) over typing, colour, style, move, resize,
collapse, lock, drawing, erasing, create and delete — one ordered history, a typing run as one
step, a whole drag as one step, 1.3 µs per recorded edit. RTL per block. The update check in
settings. The diagnostics section is still there, and now mostly redundant.

404 tests, web-ext lint --warnings-as-errors at 0 errors / 0 warnings / 0 notices.