Skip to content

Synchronous cross-world function calls: can a contextBridge-exposed function return a value synchronously on WKWebView/WebKitGTK worlds? #399

Description

@0monish

Generated by an AI agent (Claude Code, Fable 5.1) on behalf of @0monish during Wayfinder charting of the Electron compatibility program, 2026-10-06. Evidence-backed; every resolved decision was taken under explicit user delegation and cites its sources. Planning only — no implementation is authorized by this issue.

Parent map: #391 · Unit: F04 (ipc-bridge) · Wayfinder type: prototype · Status: open (not resolvable from current evidence)

Tracker of record: this GitHub issue (decision ticket; claim by self-assignment). Linear reference: KEL-80 (Backlog) carries a link here.

Question

Zettlr exposes functions whose page-visible return is produced synchronously (config.get via sendSync wrapper). Both worlds share the DOM, so a shared-DOM relay is a lead; unverified. Measure a cross-world relay (per-call cost, sync return feasibility) in the KEL-142 fixture; fallback is async-only exposure (▲) which keeps draw.io config-only.

What is known / why it is still open

No evidence-backed resolution yet; see the unit research note for the proposed answer and refuter verdicts.

Context

Epic #463 · research note wayfinder/electron-compat/notes/F04.md on the research branch.

Activity

  1. added theissue type on Oct 6, 2026
  2. 0monish commented on Oct 6, 2026

    @0monish
    MemberAuthor

    Generated by an AI agent (Claude Code, Fable 5.1) on behalf of @0monish, 2026-10-06, under the map's execution doctrine. Statements are labelled FACT / INFERENCE / UNKNOWN by their author; nothing here authorizes implementation.

    Decision packet

    Decision: Whether a contextBridge-exposed function can return its value synchronously across WKWebView content worlds (and later WebKitGTK), or whether exposed functions are Promise-returning with a labelled divergence.

    Classification: needs-experiment · Milestone (YAGNI test against the first proof): next · Reversible: yes - parking changes nothing in the tree; the relay is an internal detail behind contextBridge.exposeInMainWorld, so moving between A and B later changes only compat cells. · Owner: KEL-142 seam (Done; Linear, fetched 2026-10-06) under its parent bridge owner KEL-80 (Backlog; Linear, fetched 2026-10-06); crate keld-wv.

    Facts

    Inferences

    Unknowns

    • Whether an isolated-world listener runs synchronously inside a page-world dispatchEvent on WKWebView.
    • What CustomEvent.detail carries across worlds (null, a clone, or a shared object) and whether any JS object identity leaks.
    • Per-call cost of a DOM-string relay versus the asynchronous relay.
    • WebKitGTK behaviour (needs a Linux machine).

    Alternatives

    Option Cost New invariant created Existing invariant at risk
    A. Shared-DOM synchronous relay (page dispatches a DOM event; the preload world's listener writes the serialized result to shared DOM state the page reads back). A small WKWebView experiment, then serialization of every call through DOM strings; a second call path beside the asynchronous relay. Cross-world values travel only as serialized strings; no JS object or function identity crosses worlds. KEL-142 isolation rule that the page must not reach the native handler or document nonce (the relay must carry neither).
    B. Asynchronous-only exposure: every exposed function returns a Promise; labelled divergence with a compat diagnostic. Zettlr's sync-return functions break without source edits; draw.io is unaffected. Exposed functions are never synchronous on isolated-world engines. KEL-127 direction 'never a silent downgrade' if the label or diagnostic is missing.
    C. Run the app preload in the page world to get synchronous calls for free. Gives up engine isolation that exists today. None. KEL-142/KEL-139 isolation acceptance. Rejected.

    Recommendation

    Park for the first proof and remove the F04-A3 (#399) edge from F04-T4 (#467)'s draw.io slice (asynchronous relay with void-return functions). Before any Zettlr work, run the smallest experiment on a macOS WKWebView harness with two content worlds. Arms: (1) page calls dispatchEvent and reads the result in the same task with no await - sync return observed or not; (2) negative control - the same assertion over a postMessage relay must fail; (3) leak check - an object and a function placed in detail must not be reachable or callable from the other world; (4) re-entrancy - a preload listener calling back into a page listener during the call preserves order; (5) cost - 10,000 calls at 64 B and 4 KiB, p50/p99 recorded as a diagnostic. Exit: arms 1, 3 and 4 pass and arm 2 fails as designed gives option A; arm 1 failing gives option B as a labelled divergence. WebKitGTK is a separate Linux observation.

    Falsifier: The populated draw.io webapp reads a return value from a window.electron.* call or depends on a send completing before a synchronous unload; or arm 3 shows a shared object identity across worlds, which would make option A an isolation regression.

    Missing evidence: The experiment transcript on real WKWebView (arms 1-5); a scan of the populated draw.io webapp for uses of return values from window.electron.*.

    Next action: Orchestrator edits the map: drop F04-A3 (#399) from F04-T4 (#467)'s first-proof blockers and mark this ticket as Zettlr-gated. First observable completion check: F04-T4 (#467)'s blocked-by list for the draw.io slice no longer names F04-A3 (#399).

  3. added
    milestone:nextNeeded after the first proof (Zettlr or strict profile)
    on Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    electron-compatElectron compatibility program areamilestone:nextNeeded after the first proof (Zettlr or strict profile)wayfinder:prototypeWayfinder prototype decision

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions