Skip to content

[GUI] the window keeps away the colour emoji font WebKitGTK dies on (Linux) - #4485

Open
miketgv wants to merge 1 commit into
Niko1221:mainfrom
miketgv:gui-colrv1-emoji
Open

miketgv wants to merge 1 commit into
Niko1221:mainfrom
miketgv:gui-colrv1-emoji

Conversation

@miketgv

@miketgv miketgv commented Oct 28, 2026 •

Copy link
Copy Markdown

The problem

On Linux, the app window turns grey and stops answering when a chat draws an emoji, on any system whose colour emoji font is the COLRv1 format (Fedora 44 and newer Ubuntu ship it as the default). The window log says:

././/include/c++/12/bits/stl_vector.h:1123: ... colrv1_configure_skpaint(FT_Face, ...) ... ColorStop ...:
Assertion '__n < this->size()' failed.

The WebKitGTK inside the AppImage paints COLRv1 through Skia, and colrv1_configure_skpaint indexes past the end of a gradient's colour-stop vector. The library is built with _GLIBCXX_ASSERTIONS, so that read becomes abort() and WebKitWebProcess dies.

What makes it bad rather than merely wrong is the recovery: the shell only watches the window process, and that process can stay alive when its web process dies. Nothing is then logged, nothing reloads, no crash log is written, and the person is left with a grey box and no message. A chat that contains an emoji reproduces it on every start that restores that chat.

The approach

Keep the one font family that cannot be drawn away from the window process — nothing else.

  • The window process points fontconfig at a file it writes under its own data folder. The file pulls in /etc/fonts/fonts.conf first, so every other font resolves exactly as it does today, then rejects */Noto-COLRv1.ttf and */NotoSansCOLRv1-Regular.ttf inside <selectfont> and prefers the outline Noto Emoji for the Noto Color Emoji family. Emoji arrive as outlines instead of in colour.
  • Only the window process is told. The shell is deliberately left alone, so the host, the engine and the agent — including the agent's browser tool — keep the fonts the person chose. A colour emoji is perfectly good in a Playwright page and in every other program on the machine; this is a WebKit bug, so it is scoped to WebKit.
  • The file is written only when a COLRv1 face is actually installed, so a machine without the crashing font is untouched — same environment, same behaviour, byte for byte.
  • FONTCONFIG_FILE already set stays untouched (the person's own choice wins). STRATA_FONT_CONFIG=off turns this off; STRATA_FONT_CONFIG=/path/to/fonts.conf uses a file of theirs.
  • Written atomically through a temporary file and refreshed only when its contents differ, so fontconfig is never handed a half-written file and a leftover from an older build puts itself right.
  • The three font variables join the window's crash report next to the existing graphics settings, because which font file is in use is the whole question in this failure.

The bitmap (CBDT) and outline faces are not rejected — they draw fine, and hiding them would leave a tofu box instead of an emoji.

Validation

Passed

  • cargo test --release in the desktop shell: the 4 new tests in fontguard.rs pass — the file's contents and its <selectfont> nesting, that each reject sits inside it and that no placeholder is left unexpanded, both opt-outs and a custom file, the shallow scan (the bitmap and outline faces alone must not trigger it), and write-once-plus-refresh with no temporary left behind.
  • cargo clippy --release: no findings in the new module. (It reports 8 needless_borrow in the Linux drag-drop code, one doc list item without indentation at main.rs:22 from commit 322790af, and one dead function in shell.rs; all pre-existing, all left alone.)
  • Built inside Ubuntu 22.04 to match the AppImage baseline. The binary needs GLIBC_2.34, the same floor as the shipped strata-desktop, and carries the same RUNPATH=$ORIGIN/../lib, so it is a drop-in and no narrower than today.
  • The file the code generates, fed to real fontconfig on the machine that crashes: fc-list returns 895 fonts with it and 896 without — the single difference is Noto-COLRv1.ttf. Noto Color Emoji and emoji resolve to NotoEmoji-Regular.ttf; sans-serif, monospace, Segoe UI, Noto Sans, DejaVu Sans Mono and Courier New resolve exactly as before. Zero fontconfig warnings.
  • End-to-end in a real window process, both binaries against the AppImage's own bundled WebKit and its AppRun GTK environment, under Xvfb in Ubuntu 22.04 with the failing font installed. The new binary writes its file and the WebKitWebProcess it forks carries FONTCONFIG_FILE=…/fonts/fonts-no-colrv1.conf in its own environment; the official 0.2.7 binary's web process has the variable unset. So the guard runs, runs before WebKit forks its renderer, and reaches the process that does the drawing. Both windows stayed up and served the page: no regression from the change.

Failed (pre-existing, unchanged by this pull)

  • desktoplog::tests::a_failed_start_is_explained_in_plain_words_and_written_to_a_crash_file asserts the literal "0.2.0" while the crate is 0.2.7. It fails identically on pristine upstream/main in the same container with these files removed. One-line fix, left out because it is not this problem.

Not run

  • The crash itself, before and after. I could not reproduce the abort on demand: in the container both binaries rendered an emoji page without loading the COLRv1 face or crashing. The case for the cause is therefore the assertion naming colrv1_configure_skpaint — which exists only in the COLRv1 path and is the only colrv1_* symbol in the library — plus the log lines and coredumps from the real app, and the same assertion and the same fontconfig workaround reported independently by other AppImage projects on Fedora 44. It is not a controlled before/after.
  • The fix on a real desktop. Verified on this machine as far as the line above allows; a restart of the running app was not wanted mid-report.
  • macOS and Windows. There is no COLRv1 path there (WKWebView and WebView2, not Skia), which is why the change is #[cfg(target_os = "linux")].
  • The older SIGSEGV crashes on a JITWorker thread seen on the reporting machine. A different fault, not addressed here — see limits.

Limits

  • This works around the WebKit/Skia fault by not showing it a font it cannot handle. It does not fix colrv1_configure_skpaint, and colour emoji in the window become outline emoji. The real fix belongs upstream in WebKitGTK/Skia.
  • It hides the two known COLRv1 file names plus any file whose name contains colrv1. A COLRv1 colour font under another name would still get through.
  • It cannot tell WebKit "do not use colour fonts"; fontconfig has no finer lever than this.
  • Because I could not trigger the abort on command, the grey-screen reports that end with the window process exiting cleanly and no coredump are not covered by this pull. Those look like WebKit ending its own web process for another reason. Worth separating from this one.
  • A distinct bug stays open: the shell cannot tell "the web process died" from "the person closed the window". A renderer failure therefore reads as a normal close, and the shell stops the host and the engine with it. WebKitWebView::connect_web_process_terminated is available in the pinned webkit2gtk 2.0.2 and is used nowhere in the shell. That deserves its own issue; a workaround for one renderer fault should not pretend the recovery path is fine.

Reproduce

  1. Fedora 44 (or any system where fc-match "Noto Color Emoji" returns Noto-COLRv1.ttf).
  2. Open the 0.2.7 AppImage, ask the model for a reply containing emoji, or restore a chat that has one.
  3. The window goes grey. ~/.local/share/strata/logs/desktop-*.log shows the colrv1_configure_skpaint assertion; coredumpctl shows WebKitWebProcess with SIGABRT.

Workaround for anyone hitting it before this lands: point FONTCONFIG_FILE at a file that includes /etc/fonts/fonts.conf and rejects */Noto-COLRv1.ttf inside <selectfont>.

…inux)

The window turns grey and stops answering when a chat draws an emoji on a
system whose colour emoji font is the COLRv1 format (Fedora 44, newer Ubuntu).
The WebKitGTK inside the AppImage paints that format through Skia, and its
colrv1_configure_skpaint reads past the end of a gradient's colour-stop list;
the library is built with libstdc++ range checks, so the read is an abort() and
WebKitWebProcess is gone. The window process is still there, so the shell sees
nothing wrong and the page is never reloaded: the person has a grey box and no
message. It repeats on every start that restores a chat with an emoji in it.

The window process now points fontconfig at a file of its own that rejects only
the COLRv1 faces and prefers the outline "Noto Emoji", so emoji arrive as
outlines instead of colour. It pulls in /etc/fonts/fonts.conf first, so every
other font resolves exactly as before, and it is set only in the window process:
the host, the engine and the agent keep the fonts the person chose. The file is
written under the app's own data folder, atomically, and only when a COLRv1 font
is actually installed, so a machine without one is untouched. FONTCONFIG_FILE
stays the person's own if they set it, and STRATA_FONT_CONFIG=off turns this off.

The three font variables go into the window's crash report beside the other
graphics settings, because which font file is in use is the whole question here.

Checked on Fedora 44 with the font that fails: fontconfig lists 895 fonts with
the file and 896 without, the one difference being Noto-COLRv1.ttf; "Noto Color
Emoji", "emoji" and the ordinary text families all resolve, with no fontconfig
warnings. Four unit tests cover the file's contents and nesting, the opt-outs,
the scan, and the write-and-refresh. cargo clippy is clean for the new module.
Not checked: the real window on a real desktop, and the older SIGSEGV crashes on
the JITWorker thread, which are a separate fault.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant