The owner, watching finger pans: "the center column is scrolling/panning smoothly but the sidebars are not". Between frames the card moved the whole frame it holds, desktop Wikipedia's sticky sidebars with it, and each new frame put them back. Engine 0068 returns each frame's fixed and stuck rects with its poll; the daemon writes them and the view row into the 192 bytes before the segment trailer (JihadStuckBlock, tagged with the frame's number; JIHAD_GPU_NO_HOLD=1 off); the adapter draws them where the frame drew them and covers their moved copy in the page's colour beside them. Four mid-drag screenshots each way: sidebars at +0 px in all four with it (the column moved up to 150 px), displaced 11-12 px in two of four without; no ghost; frame rate unchanged. R3's pan criterion is re-opened against the corrected test (the owner's choice): with the card moving desktop Wikipedia pans at ~7 fps. The card-redraw throttle prototype showed no gain and is not kept. UXP_PIN and SRCREV at 078a0942. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KP7JxhNqPrvYp27QDy8hP8
render/browserserver — engine-agnostic render daemon + IPC
Derived from the isis-project BrowserServer / BrowserAdapter / AdapterBase (Apache-2.0). This directory holds the parts of the render daemon that are independent of the rendering engine and are kept unchanged across the QtWebKit → Goanna swap.
Self-contained naming: the daemon's YAP server name is set from
JIHAD_BS_NAME(Main.cpp), so it serves/tmp/yapserver.jihad-browserinstead of the stockbrowsersocket — letting Jihad coexist with the stock browser rather than replace it. The YAP command/message interface is byte-identical; only the socket name differs. The matching adapter is built asBrowserAdapterJihad.so(MIMEapplication/x-jihad-browser). See../../packaging/README.mdand auto-memoryjihad-self-contained-arch.md.
Runtime state (T-057, cavekit-device-build R8): the daemon derives ONE variant-scoped state directory from
JIHAD_BS_NAMEin../goanna/JihadRuntimePaths.h—/var/palm/jihad/{enyo,mochi,mojo}/on the device (root-owned 0755), percontext/plans/plan-variant-identity.md. The engine profile/cache, the frame dump and the self-drive inject channel all live there; nothing is written to/media/internal(the user's USB mass-storage volume). Off-device (no writable/var/palm) it falls back to$HOME/.jihad/<variant>/, then/tmp/jihad-<uid>/<variant>/. Env knobs:JIHAD_STATE_DIRoverrides the directory;JIHAD_INJECT=1turns the DEBUG self-drive channel ON (off by default — seeJihadBrowserServer.cpp);JIHAD_DUMP=1writes<state>/frame.ppm;JIHAD_PROFILE_DIRoverrides the profile.
Termination (2026-08-04):
Main.cpphandles SIGTERM/SIGINT/SIGHUP — the handler only sets a flag, and the 16 ms tick quits the GLib loop, becauseg_main_loop_quitis not async-signal-safe. This is not hygiene.stop <job>sends SIGTERM, and the engine's state is written by DEFERRED savers (the add-on database, prefs) that only flush during XPCOM shutdown; with the default action,main()never returned,XRE_TermEmbeddingnever ran, and those changes were lost — an extension disabled fromabout:addonscame back enabled.main()also SCOPES the server so every page (andnsIWebBrowser) is destroyed before the runtime; the other order crashes in engine teardown. Two never-executed teardown paths were broken when they first ran:YapServerPriv::deadlockDetectoris uninitialized andrun()'s default never creates it, yet~YapServerdeletes it anyway; and~YapServerDeadlockPrivquit a null loop and joined thread 0. Anything you add on the shutdown path is, until proven otherwise, untested code.
The upstream sources are cloned for reference at
../../../ref-BrowserServer,../../../ref-BrowserAdapter,../../../ref-AdapterBase. During Phase 1 the reusable files are imported here (keeping their Apache-2.0 headers); the QtWebKit-specific files are replaced by the Goanna backend in../goanna.
Keep (engine-agnostic)
- YAP server / IPC:
BrowserServerBase.{h,cpp}(autogenerated interface),IpcBuffer,BrowserSyncReplyPipe, the YAP dispatch. Do not hand-edit — regenerate. The generator and the authoritative.defsare both IN this tree atCodeGen/(seeCodeGen/README.mdfor the build + regenerate loop); the upstream copies in../../../ref-BrowserServer/CodeGenare STALE and must not be used (measured 2026-08-10: that.defshas no 0x1600 and reverts KeyDown/KeyUp to twouint16_t; that generator emits every async command as= 0;). Command 0x1600asyncCmdSetExtraBufferwas added on 2026-08-10 to announce a third shared framebuffer, becauseconnect/thawcarry exactly two keys and their shapes are frozen. It is declared non-pure-virtual with an empty default body so a server without a third buffer still compiles; since 2026-08-15 the vendored generator emits that body from theoptionalflag in the.defs, so a regeneration needs no hand edit.build/webos-oe/check-yap-contract.shfails the build on any of it. Rationale and measurements:docs/IPC-CONTRACT.md. - Daemon & lifecycle:
Main.cpp,BrowserServer.{cpp,h}(minus QtWebKit bits),BrowserServerBase,BrowserPageManager,CpuAffinity,PluginDirWatcher,Settings. - Shared framebuffer plumbing: the shmem attach/return/double-buffer logic
and
BrowserOffscreenInfopixel-format contract (the interface; the Qt implementationBrowserOffscreenQtis replaced). - LunaService surface:
palm://com.palm.browserServer/{clearCache,clearCookies}. - Backup manager, SSL/cert glue (
BackupManager,SSLSupport,SSLValidationInfo) — re-pointed at Goanna's cert APIs. - JSON utils (
JsonUtils, pbnjson usage).
Replace (QtWebKit-specific)
BrowserPage.{cpp,h}—QGraphicsView/QGraphicsWebView/WebOSWebPageinternals →render/goanna/BrowserPageGoanna(nsIWebBrowser+ offscreen widget). The public method surface ofBrowserPageis preserved so the YAP command dispatch inBrowserServerBasecalls it unchanged.BrowserOffscreenQt.{cpp,h}— Qt pixmap/QImage surface → a Goanna surface that paints into the same shmem buffer (BrowserOffscreenInfo).WebKitEventListener.{cpp,h}— QtWebKit signal handlers → GoannansIWebProgressListener/chrome listeners in the Goanna backend.- Qt build deps (
qbsinterface,weboswebpage.hfrom webkit-supplemental).
Boundary
BrowserServerBase dispatches each YAP command to a BrowserPage*. We keep
that vtable identical and provide it from the Goanna backend. That single
boundary is what makes "swap the core, keep the IPC" mechanically possible.