Skip to content

feat: create(html:) — one-step create directly on authored HTML#21

Merged
wenkaifan0720 merged 1 commit into
mainfrom
feat/create-with-html
Jul 3, 2026
Merged

feat: create(html:) — one-step create directly on authored HTML#21
wenkaifan0720 merged 1 commit into
mainfrom
feat/create-with-html

Conversation

@wenkaifan0720

Copy link
Copy Markdown
Collaborator

Why

Hosts that inject an authored document (like Campus's agent_ui tiles) currently do a two-step load: create('about:blank'), then later loadHtmlString(doc) once the browser resolves. That second step races create-resolution, body-mount, the one-shot onPageStarted, and visibility — the well-known nondeterministic blank tile. A plain cefWebview does ONE step (create(realURL)) and is rock-solid.

This makes the authored-doc path structurally identical to a webview: the browser is born on the document, so there is no second step to race and the tile can never be blank (worst case a stale doc, corrected by a normal reload).

What

  • CefWebController.create({String? html}) / CefWebView(html: …) — when html is set, the browser is created directly on a data:text/html;charset=utf-8;base64,… URL built from the doc (same encoder loadHtmlString now uses).
  • Native (cef_host/main.mm): when the create URL is a host-trusted content-injection scheme (data: / file:) and a scheme allowlist is active, auto-arm the trusted_pending exemption for that URL so the initial load isn't refused by OnBeforeBrowse. These schemes can never arise from untrusted page navigation, so this is safe.
  • Protocol bumped to v3 (host + Swift) so a stale cef_host is refused loudly (protocolMismatch) rather than silently blanking.
  • Unit test: create(html:) creates on the authored data: URL and issues no loadTrusted follow-up. 40 tests pass.

Validated end-to-end in the example app against a v3 host: the authored HTML renders directly from the data: create URL, zero refusals.

…s the agent_ui blank race)

A consumer can now create a CEF browser DIRECTLY on an authored document instead of
create('about:blank') + a later loadHtmlString. The two-step form is inherently racy (the second
load depends on create-resolution, body-mount, a one-shot onPageStarted, and visibility all
interleaving right) — the root cause of nondeterministic blank agent_ui tiles. One-step create makes
an html tile structurally identical to a URL tile (cefWebview), which is rock-solid.

- cef_host (main.mm): a data:/file: create URL — host-trusted content-injection schemes that can
  NEVER arise from an untrusted page navigation (the allowlist refuses them in OnBeforeBrowse) — arms
  the trusted-load exemption for its INITIAL load, exactly as DoNavigateTrusted does for a post-create
  loadTrusted. Same trust model; only the timing (at create) differs. Protocol bumped 2 -> 3 so a
  stale host without this is refused at the handshake rather than silently blanking a data: create.
- CefWebController.create gains `html:` (base64 data: URL, shared with loadHtmlString via _htmlDataUrl).
- CefWebView gains `html:` (routes to create(html:)).

Tests: create(html:) emits the trusted data: URL as the sole create, no separate loadTrusted; suite
40 green. Changes native -> new content hash -> republish + consumer pin bump.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@wenkaifan0720 wenkaifan0720 merged commit f902941 into main Jul 3, 2026
1 check passed
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