diff --git a/PORTING.md b/PORTING.md
index 7ad82b7..57d8152 100644
--- a/PORTING.md
+++ b/PORTING.md
@@ -107,17 +107,16 @@ Reference: `packages/flutter_cef_macos/native/cef_host/` (`main.mm` is the
browser process, `process_helper.mm` the CEF child processes). The CEF client /
app / handler logic, the browser-control functions, the navigation scheme
allowlist, and the **IPC opcode protocol** are platform-agnostic C++ and can be
-reused verbatim. Only these seams are macOS-specific (file:line are into
-`main.mm` at the time of writing):
+reused verbatim. Only these seams are macOS-specific:
| Seam | macOS reference | What your platform provides |
| --- | --- | --- |
-| **Shared surface** — receive painted frames and present them to the host texture. CEF delivers either software `OnPaint` (CPU buffer) or `OnAcceleratedPaint` (a shared GPU texture handle). | `OnPaint` / `OnAcceleratedPaint` + the `IOSurface*` ops (~lines 346–450); `g_surface` (~133). macOS uses an IOSurface-backed `CVPixelBuffer`. | **Windows**: a shared D3D11 texture. Note (verified against CEF 144 `cef_types_win.h` + Flutter engine `external_texture_d3d.cc`): CEF's `OnAcceleratedPaint` delivers an **NT** handle (no keyed mutex, non-owning, pool-released when the callback returns — open it synchronously via `ID3D11Device1::OpenSharedResource1`), while Flutter's ANGLE compositor requires a **legacy** `D3D11_RESOURCE_MISC_SHARED` handle (`EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE`); the two are mutually exclusive on one texture, so exactly one `CopyResource` NT→legacy is structurally mandatory (macOS also blits — `CompositeMetalLocked`). **Linux**: shared memory or a DMA-buf, presented via the platform texture. Either way the model is **producer-allocates**: `cef_host` mints the shared surface and announces it via `opPresent` — the host does NOT pass a surface id in the create (the macOS field name differs too: `shared_texture_io_surface` vs Windows `shared_texture_handle`, so the accelerated-paint glue takes a per-OS `#if`, not a typedef). |
-| **IPC transport** — a framed bidirectional byte stream to the host. Wire format: 4-byte big-endian length prefix, then `[u32 browserId][opcode][payload]` (`browserId 0` = process-level: `opReady`, process logs, `opShutdown`). | `WriteAll`/`ReadAll` (207–235), `SendFrame` (~237–249), `ConnectUnixSocket` (1341+), the read loop (~1140). Unix domain socket. | **Windows**: a named pipe. **Linux**: a Unix domain socket (reuse as-is). Keep the same length-prefixed framing and the `browserId` dimension. |
-| **App / run loop** — give CEF a host application + message loop, and a per-profile cache dir. | `CefHostApplication : NSApplication
)}` on success or `{ok:false,v:String(e)}` on throw, with
`request = "eval::"`.
-- `OnQuery` (main.mm:1481-1486) refuses subframe queries (403), strips the
+- `OnQuery` refuses subframe queries (403), strips the
`"eval:"` prefix (5 bytes), and sends **0x16 kOpEvalResult** `{utf8
- "id:json"}` (main.mm:1483). The plugin emits `evalResult
+ "id:json"}`. The plugin emits `evalResult
{payload:"id:json"}` (§4); Dart splits at the FIRST `:`, matches the pending
completer by `id`, and JSON-decodes the tail: `ok:true` completes with `v`,
- `ok:false` completes with an `Exception('')` (`_handleEvalResult`,
- cef_web_controller.dart:297-313).
-- In-flight evals are failed (never leaked) on `pageStarted`
- (cef_web_controller.dart:224-228), `processGone`, and `dispose`
- (cef_web_controller.dart:317-324).
+ `ok:false` completes with an `Exception('')` (`_handleEvalResult`).
+- In-flight evals are failed (never leaked) on `pageStarted`, `processGone`,
+ and `dispose` (`_failPendingEvals`).
- `executeJavaScript(code)` is the fire-and-forget sibling → **0x25
kOpExecuteJs** `{utf8 code}` (§2), no result event.
### 7.4 JS dialogs — alert / confirm / prompt
-- Host → page request: `CefJSDialogHandler::OnJSDialog` (main.mm:1279-1303)
+- Host → page request: `CefJSDialogHandler::OnJSDialog`
assigns a per-slot `id`, stashes the `CefJSDialogCallback`, and sends **0x0f
- kOpJsDialog** `{u32 id}{u32 type}{u32 msgLen}{msg utf8}{defaultText utf8}`
- (main.mm:1291-1301). `type`: 0=alert, 1=confirm, 2=prompt (main.mm:1286-1288).
+ kOpJsDialog** `{u32 id}{u32 type}{u32 msgLen}{msg utf8}{defaultText utf8}`.
+ `type`: 0=alert, 1=confirm, 2=prompt.
The plugin emits `jsDialog {id,type,message,defaultText}` (§4).
-- Dart (`_handleJsDialog`, cef_web_controller.dart:345-382) routes by `type` to
+- Dart (`_handleJsDialog`) routes by `type` to
`onJavaScriptAlertDialog` / `onJavaScriptConfirmDialog` /
`onJavaScriptTextInputDialog`, then replies verb `respondJsDialog
{id, ok, text}` → **0x29 kOpJsDialogResp** `{u32 id}{u8 ok}{utf8 text}` (§2).
Unset handlers fall closed to sensible defaults (alert dismissed, confirm→OK,
prompt→defaultText). A throwing handler fails closed (`ok=false`) but is
reported via `FlutterError.reportError` (not silently swallowed).
-- Host applies the answer: `DoJsDialogResp` (main.mm:1994-2000) looks up the
+- Host applies the answer: `DoJsDialogResp` looks up the
stashed callback by `id` and calls `Continue(ok, text)`, which returns the
page's `alert`/`confirm`/`prompt`. `OnBeforeUnloadDialog` always allows
- navigation (main.mm:1304-1309).
+ navigation.
### 7.5 Find-in-page
- `find(text, forward, matchCase, findNext)` → verb `find` → **0x27 kOpFind**
- `{u8 fwd}{u8 matchCase}{u8 findNext}{utf8 text}` (§2,
- cef_web_controller.dart:858-868; host read main.mm:2452-2459 → `DoFind`).
+ `{u8 fwd}{u8 matchCase}{u8 findNext}{utf8 text}` (§2; host → `DoFind`).
- `stopFind(clearSelection)` → verb `stopFind` → **0x28 kOpStopFind** `{u8
- clearSelection}` (absent = 1) (§2, cef_web_controller.dart:871-872; host
- main.mm:2460-2465 → `DoStopFind`, main.mm:1991-1993).
-- Result event: `CefFindHandler::OnFindResult` (main.mm:1260-1275) sends **0x0e
- kOpFindResult** `{u32 count}{u32 activeOrdinal}{u8 final}` = 9 bytes
- (main.mm:1265-1274). The plugin emits `findResult
+ clearSelection}` (absent = 1) (§2; host → `DoStopFind`).
+- Result event: `CefFindHandler::OnFindResult` sends **0x0e
+ kOpFindResult** `{u32 count}{u32 activeOrdinal}{u8 final}` = 9 bytes.
+ The plugin emits `findResult
{count, activeMatchOrdinal, isFinal}` (§4); Dart delivers a `CefFindResult`
- to `onFindResult` (cef_web_controller.dart:239-245). ⌘F/Ctrl+F is surfaced to
- the host via `CefWebView.onFind` (cef_web_view.dart:571-579) — the widget has
+ to `onFindResult`. ⌘F/Ctrl+F is surfaced to
+ the host via `CefWebView.onFind` — the widget has
no find bar of its own.
### 7.6 Content zoom
- `setZoomLevel(level)` → verb `setZoomLevel` → **0x26 kOpSetZoom** `{f64
- level}` (§2, cef_web_controller.dart:822-823; host read main.mm:2434-2438 →
- `DoSetZoom`). `level` is a Chromium zoom LEVEL; the factor is `1.2^level`
- (0 = 100%). The view wires Ctrl/⌘ +/-/0 to step it (cef_web_view.dart:559-570).
+ level}` (§2; host → `DoSetZoom`). `level` is a Chromium zoom LEVEL; the
+ factor is `1.2^level` (0 = 100%). The view wires Ctrl/⌘ +/-/0 to step it.
No result event.
### 7.7 Downloads
-- `CefDownloadHandler::OnBeforeDownload` (main.mm:1251-1257) allows the download
+- `CefDownloadHandler::OnBeforeDownload` allows the download
(CEF blocks downloads without a handler) and sends **0x18 kOpDownload** `{utf8
- suggestedName}` (main.mm:1254). The plugin emits `download {suggestedName}`
- (§4); Dart invokes `onDownload(suggestedName)`
- (cef_web_controller.dart:255-257). Informational only (no reply verb).
+ suggestedName}`. The plugin emits `download {suggestedName}`
+ (§4); Dart invokes `onDownload(suggestedName)`.
+ Informational only (no reply verb).
- Windows continues with `show_dialog=true`, like macOS's Save panel, so
nothing is written without the user's say. The dialog opens on
`%USERPROFILE%\Downloads\`, where the leaf is the page's suggested name
@@ -535,7 +531,7 @@ Page → host. The shim is injected NATIVELY (there is no Dart-injected shim):
dots/spaces and DOS device names neutralized) and given a ` (n)` suffix when
that file exists.
-## 8. Agent control — CDP-over-pipe + the token-gated loopback relay (P9)
+## 8. Agent control — CDP-over-pipe + the token-gated loopback relay
An external CDP client (Playwright via `connectOverCDP`, agent-browser) drives a
live, logged-in tile **without** an open debug port: Chromium speaks CDP over an
@@ -543,14 +539,14 @@ inherited pipe (`--remote-debugging-pipe` + `--remote-debugging-io-pipes`,
NUL-framed JSON), and a small token-gated LOOPBACK HTTP+WebSocket relay bridges a
standard CDP client to that pipe. Transcribed from the macOS reference
`CdpRelay.swift` (the canonical relay) + `CefProfileHost.swift`
-(launchViaPosixSpawn / readCdpLoop / enableAgentControl); the Windows spawn is
-the S3 recipe (`flutter_cef_spikes/s3`). **SINGLE-TILE scope:** one relay per
-host (raw browser-level passthrough — the pipe carries exactly one page target);
-the per-tile Target-domain filter + N-relay CDP-id multiplex (CEF-2b,
-`CdpRelay.swift:560-884`) is a documented follow-up (the `scope_target_id` seam
-in `windows/cdp_relay.h`).
+(launchViaPosixSpawn / readCdpLoop / enableAgentControl); the Windows spawn
+hands Chromium two inherited anonymous pipes instead of fds. **SINGLE-TILE
+scope:** one relay per host (raw browser-level passthrough — the pipe carries
+exactly one page target); the per-tile Target-domain filter + N-relay CDP-id
+multiplex (the `scopeTargetId` machinery in `CdpRelay.swift`) is a documented
+follow-up (the `scope_target_id` seam in `windows/cdp_relay.h`).
-### 8.1 Launch — the CDP pipe (S3 recipe)
+### 8.1 Launch — the CDP pipe
The `create` arg `agentControl:true` (§3) switches the host launch mechanism.
`HostProcess::Spawn(agent_control=true)` (windows/host_process.cpp):
@@ -563,14 +559,14 @@ The `create` arg `agentControl:true` (§3) switches the host launch mechanism.
inherits nothing: the IPC pipe is connected by NAME (`CreateFileW`) and the Job
Object is assigned post-spawn — neither is an inherited handle. `bInheritHandles
= TRUE` + `EXTENDED_STARTUPINFO_PRESENT` are added only on this path; the
- non-agent spawn stays byte-identical (S2/S3 confirm bootstrap preserves the
- handle list).
+ non-agent spawn stays byte-identical (the renamed bootstrap exe passes the
+ inherited handles through to the host DLL unchanged).
- The child gets `--cdp-io-pipes=,` (decimal HANDLE
values). cef_host's `OnBeforeCommandLineProcessing` (browser process only)
translates it into Chromium's `--remote-debugging-pipe` +
- `--remote-debugging-io-pipes=,`. Mirrors macOS main.mm's
- `--cdp-pipe` → `remote-debugging-pipe` injection (fds 3/4 there; explicit HANDLE
- values here).
+ `--remote-debugging-io-pipes=,`. Mirrors the macOS
+ `main.mm` `--cdp-pipe` → `remote-debugging-pipe` injection (fds 3/4 there;
+ explicit HANDLE values here).
- The plugin keeps the two PARENT-side ends (`CdpTransport::read`/`write`) and
runs an always-on `CdpReadLoop` that splits the NUL-framed CDP stream and fans
each message to the current relay (mirrors `CefProfileHost.readCdpLoop` +
@@ -615,10 +611,10 @@ multi-client `--remote-debugging-port`.
### 8.4 Deferred (N-tile Target multiplex)
-Not implemented in the slice (single-tile is passthrough): `Target.getTargetInfo`
+Not implemented on Windows (single-tile is passthrough): `Target.getTargetInfo`
targetId resolution (`kOpResolveTargetId 0x36` → `kOpTargetId 0x1b`, still a
logged drop host-side), the deny-by-default / fail-closed / flatten-only
Target-domain filter, and the per-relay CDP-id rewrite/demux that lets N relays
-share one browser-wide pipe. See `CdpRelay.swift:560-884` +
-`CefProfileHost.swift:1460-1596` and the filter test vectors
-`packages/flutter_cef_macos/macos/Classes/test/CdpRelayFilterTests.swift`.
+share one browser-wide pipe. See `CdpRelay.swift` + `CefProfileHost+Cdp.swift`
+and the filter test vectors
+`packages/flutter_cef_macos/test/CdpRelayFilterTests.swift`.
diff --git a/packages/flutter_cef_windows/native/cef_host/cef_host_protocol.h b/packages/flutter_cef_windows/native/cef_host/cef_host_protocol.h
index c9edc0c..76e9827 100644
--- a/packages/flutter_cef_windows/native/cef_host/cef_host_protocol.h
+++ b/packages/flutter_cef_windows/native/cef_host/cef_host_protocol.h
@@ -18,11 +18,11 @@
namespace flutter_cef {
-// Framing guard (main.mm:2347): minimum body = 4 (browserId) + 1 (op).
+// Framing guard, as on macOS: minimum body = 4 (browserId) + 1 (op).
constexpr uint32_t kMinBodyLen = 5;
constexpr uint32_t kMaxBodyLen = 64u << 20; // 64 MiB
-// ---- Big-endian codecs (mirror main.mm ReadU32BE/WriteU32BE/ReadF64BE) ----
+// ---- Big-endian codecs (mirror the macOS host's ipc.h) ----
inline uint32_t ReadU32BE(const uint8_t* p) {
return (uint32_t(p[0]) << 24) | (uint32_t(p[1]) << 16) |
diff --git a/packages/flutter_cef_windows/native/cef_host/cef_host_win.cc b/packages/flutter_cef_windows/native/cef_host/cef_host_win.cc
index 573b87c..c49bb44 100644
--- a/packages/flutter_cef_windows/native/cef_host/cef_host_win.cc
+++ b/packages/flutter_cef_windows/native/cef_host/cef_host_win.cc
@@ -3,24 +3,24 @@
// The Flutter plugin (packages/flutter_cef_windows/windows/) spawns one
// cef_host per profile and drives N browsers in it over a named-pipe IPC.
// The wire contract is PROTOCOL.md + cef_host_protocol.h in this directory —
-// transcribed verbatim from the macOS reference
-// packages/flutter_cef_macos/native/cef_host/main.mm, with ONE payload
-// difference (LAW 10): kOpPresent carries {u64 bridgeHandle BE}{u32 srcW BE}
+// transcribed verbatim from the macOS reference host
+// (packages/flutter_cef_macos/native/cef_host/), with ONE payload
+// difference: kOpPresent carries {u64 bridgeHandle BE}{u32 srcW BE}
// {u32 srcH BE} where bridgeHandle is the DXGI LEGACY shared handle of the
// host-minted D3D11_RESOURCE_MISC_SHARED bridge texture.
//
-// Ship shape (LAW 8, SPIKES.md S2): this builds as cef_host.dll exporting
+// Ship shape: this builds as cef_host.dll exporting
// RunConsoleMain, loaded by CEF's prebuilt bootstrapc.exe shipped RENAMED to
// cef_host.exe beside it. bootstrapc's sandbox_info is forwarded to BOTH
// CefExecuteProcess and CefInitialize, so the renderer, GPU and utility
// children run sandboxed. FLUTTER_CEF_NO_SANDBOX=1 turns the sandbox off.
//
-// THE LAWS this file keeps (specs/windows-port/SPIKES.md):
+// Rules this file keeps (measured on CEF 144 during the Windows port):
// 1. external_begin_frame_enabled = FALSE; windowless_frame_rate = 60.
-// (S4: with the external pump only the FIRST browser in the process ever
-// paints. The macOS PumpBeginFrame pacer is NOT ported; everywhere main.mm
-// calls SendExternalBeginFrame we use Invalidate(PET_VIEW) — with the
-// internal frame timer ON that is sufficient to drive a repaint.)
+// (With the external pump only the FIRST browser in the process ever
+// paints. The macOS PumpBeginFrame pacer is NOT ported; everywhere the
+// macOS host calls SendExternalBeginFrame we use Invalidate(PET_VIEW) —
+// with the internal frame timer ON that is sufficient to drive a repaint.)
// 2. OnAcceleratedPaint's NT handle is valid ONLY inside the callback:
// OpenSharedResource1 + CopyResource to the legacy bridge INSIDE the
// callback, synchronously. The NT handle is never stored.
@@ -33,8 +33,9 @@
// consumer contract, PROTOCOL.md §5) can refuse stale-size frames.
// 5. Bridge textures are D3D11_RESOURCE_MISC_SHARED (LEGACY handle via
// IDXGIResource::GetSharedHandle, not NT) — what ANGLE/Flutter accepts.
-// 6. (Plugin-side; supported here by keeping the retired bridge alive until
-// the present announcing its replacement has been written to the pipe.)
+// 6. The plugin holds its own opened reference to the bridge it shows; this
+// side helps by keeping the retired bridge alive until the present
+// announcing its replacement has been written to the pipe.
//
// Args (per-PROCESS / per-profile, as on macOS), read from the wide command
// line as UTF-8:
@@ -110,29 +111,31 @@ using namespace flutter_cef; // opcodes + BE codecs (cef_host_protocol.h)
using Microsoft::WRL::ComPtr;
// ---- Shared runtime state ----
-// The IPC pipe handle. Atomic for the same reason main.mm:166-172 makes the
-// fd atomic: the reader thread, SendFrame (any CEF thread), and teardown all
+// The IPC pipe handle. Atomic for the same reason the macOS host's g_ipc_fd
+// (ipc.mm) is: the reader thread, SendFrame (any CEF thread), and teardown all
// touch it.
std::atomic g_ipc_pipe{INVALID_HANDLE_VALUE};
std::mutex g_ipc_write_mutex;
// One hidden window per host process, passed to SetAsWindowless(parent) so
-// dialogs/menus/IMM degrade gracefully (PLAN §4.3; S5 works either way but
-// an HWND is the better default).
+// dialogs/menus/IMM degrade gracefully (the WebAuthn passkey UI shows either
+// way, but an HWND is the better default).
HWND g_hidden_hwnd = nullptr;
// Host-set navigation scheme allowlist (lowercased; --allowed-schemes=a,b).
// Empty = allow all. `about` is always allowed. Enforced in
-// HostClient::OnBeforeBrowse exactly like main.mm:335-342/1528-1565.
+// HostClient::OnBeforeBrowse exactly like the macOS host (SchemeAllowed in
+// host_state.mm, OnBeforeBrowse in host_client.mm).
std::set g_allowed_schemes;
-// Agent-control CDP-over-pipe (P9). "," decimal inherited-HANDLE
-// values from the plugin's --cdp-io-pipes= switch (the S3 recipe): the browser
-// READS CDP commands from and WRITES responses/events to . Set in
-// RunConsoleMain (browser process only) BEFORE CefInitialize; OnBeforeCommand-
-// LineProcessing then injects Chromium's --remote-debugging-pipe +
-// --remote-debugging-io-pipes. Empty = agent control off (byte-identical to the
-// pre-P9 launch). Mirrors macOS main.mm's --cdp-pipe translation.
+// Agent-control CDP-over-pipe. "," decimal inherited-HANDLE
+// values of two anonymous pipes, from the plugin's --cdp-io-pipes= switch: the
+// browser READS CDP commands from and WRITES responses/events to
+// . Set in RunConsoleMain (browser process only) BEFORE CefInitialize;
+// OnBeforeCommandLineProcessing then injects Chromium's --remote-debugging-pipe
+// + --remote-debugging-io-pipes. Empty = agent control off (byte-identical to
+// a launch without agent control). Mirrors the macOS host's --cdp-pipe
+// translation (OnBeforeCommandLineProcessing in main.mm).
std::string g_cdp_io_pipes;
// JS channels: on each MAIN-frame load OnLoadStart injects a
@@ -219,7 +222,7 @@ void LogErr(const char* fmt, ...) {
va_end(ap);
}
-// ---- Pipe I/O (framing per PROTOCOL.md §1 / main.mm:416-446) ----
+// ---- Pipe I/O (framing per PROTOCOL.md §1) ----
//
// OVERLAPPED, EMPIRICALLY REQUIRED (found via pipe_probe, 2026-07-20): on a
// SYNCHRONOUS pipe handle Windows serializes all I/O on the file object, so
@@ -275,10 +278,10 @@ bool WriteAllPipe(HANDLE pipe, const void* buf, size_t len) {
// Frame layout: [u32 bodyLen BE][u32 browserId BE][u8 opcode][payload].
// bodyLen = 4 + 1 + payloadLen. Assembled whole + written under the write
-// mutex so a partial write never desyncs the peer (mirrors main.mm SendFrame).
-// C3 ordering: the handle is snapshotted UNDER the write lock; teardown
-// exchanges INVALID_HANDLE_VALUE + closes under this same lock, so a late
-// paint-thread send can never write into a recycled handle.
+// mutex so a partial write never desyncs the peer (mirrors the macOS host's
+// SendFrame in ipc.mm). Ordering: the handle is snapshotted UNDER the write
+// lock; teardown exchanges INVALID_HANDLE_VALUE + closes under this same lock,
+// so a late paint-thread send can never write into a recycled handle.
void SendFrame(uint32_t browser_id, uint8_t opcode, const void* payload,
size_t payload_len) {
if (g_ipc_pipe.load() == INVALID_HANDLE_VALUE) return; // racy early-out
@@ -387,10 +390,10 @@ std::string TempDirUtf8() {
// ---- Process-wide D3D11 device for the bridge-blit present path ----
// One device + immediate context for the whole cef_host process, created
-// lazily on the first accelerated paint (mirrors main.mm's g_mtl_device
-// singleton, main.mm:320-333). The immediate context is NOT thread-safe;
-// OnAcceleratedPaint arrives on the CEF UI thread only, but g_d3d_mutex
-// serializes it anyway (belt + suspenders, and future-proof).
+// lazily on the first accelerated paint (mirrors the macOS host's
+// g_mtl_device singleton in render_handler.mm). The immediate context is NOT
+// thread-safe; OnAcceleratedPaint arrives on the CEF UI thread only, but
+// g_d3d_mutex serializes it anyway (belt + suspenders, and future-proof).
ComPtr g_d3d_device;
ComPtr g_d3d_device1;
ComPtr g_d3d_ctx;
@@ -462,9 +465,9 @@ struct Slot;
using BrowserOp = std::function;
// Per-browser state: one cef_host process multiplexes N browsers, one Slot
-// per plugin-assigned wire id (mirrors main.mm's Slot, with the IOSurface/Metal
-// fields swapped for the D3D11 bridge and the macOS-only begin-frame-pump
-// fields dropped per LAW 1).
+// per plugin-assigned wire id (mirrors the macOS host's Slot in host_state.h,
+// with the IOSurface/Metal fields swapped for the D3D11 bridge and the
+// begin-frame-pump fields dropped: this host never uses external begin frames).
//
// The slot is registered by the IPC reader when the create frame arrives, so
// every frame behind it (a resize, zoom, JS, input) finds it. The browser binds
@@ -472,7 +475,7 @@ using BrowserOp = std::function;
struct Slot {
uint32_t browser_id = 0; // plugin-assigned wire id (>=1); NOT GetIdentifier().
CefRefPtr browser;
- // H3 async-create dispose-loss guard: a dispose arriving while CreateBrowser
+ // Async-create dispose-loss guard: a dispose arriving while CreateBrowser
// is in flight records intent here; OnAfterCreated honors it the instant the
// browser binds. UI-thread-confined.
bool close_requested = false;
@@ -486,24 +489,26 @@ struct Slot {
double created_dpr = 1.0;
// Guards bridge / width / height / dpr for THIS browser. Per-slot so paints
- // on independent browsers don't contend (main.mm surface_mutex).
+ // on independent browsers don't contend (as the macOS Slot's surface_mutex).
std::mutex surface_mutex;
- // The host-minted legacy MISC_SHARED bridge texture (LAWs 3/5) — the
- // identity Flutter sees. Re-minted whenever CEF paints at a new size
+ // The host-minted legacy MISC_SHARED bridge texture (legacy handle because
+ // that is what ANGLE/Flutter accepts) — the identity Flutter sees, since
+ // CEF's own handle values alias. Re-minted whenever CEF paints at a new size
// (producer-allocates: the bridge always matches the painted frame, so the
// CopyResource below is always 1:1 — the wrong-size class is structurally
- // gone, same rationale as main.mm EnsureSurfaceForPaint:749-785).
+ // gone, same rationale as the macOS host's EnsureSurfaceForPaint in
+ // render_handler.mm).
ComPtr bridge;
uint64_t bridge_handle = 0; // IDXGIResource::GetSharedHandle of `bridge`
int bridge_w = 0;
int bridge_h = 0;
// g_d3d_epoch the current `bridge` was minted at. A mismatch means the device
- // it lives on was lost/reset, so the bridge must be re-minted (#9).
+ // it lives on was lost/reset, so the bridge must be re-minted.
uint64_t bridge_epoch = 0;
- // Belt-1 friendliness: the OLD bridge is kept alive here across a re-mint
- // until the kOpPresent announcing its replacement has been written to the
- // pipe (the plugin also holds its own opened D3D reference — LAW 6 — this
- // is the producer-side half of that belt).
+ // The OLD bridge is kept alive here across a re-mint until the kOpPresent
+ // announcing its replacement has been written to the pipe (the plugin also
+ // holds its own opened D3D reference to the bridge it shows; this is the
+ // producer-side half of keeping a shown texture alive).
ComPtr retired_bridge;
// Set under surface_mutex in OnBeforeClose BEFORE releasing `bridge`, so a
// paint racing teardown doesn't re-mint a bridge for a closing browser.
@@ -523,8 +528,9 @@ struct Slot {
double dpr = 1.0;
// Exact URLs armed for a host-trusted content load (kOpLoadTrusted /
- // data:-file: create). Exact-URL matched + consumed in OnBeforeBrowse —
- // see main.mm:222-233 for why it is URL-bound, not a one-shot flag.
+ // data:-file: create). Exact-URL matched + consumed in OnBeforeBrowse. It is
+ // URL-bound, not a one-shot flag, because OnBeforeBrowse arrives as a later
+ // task and a page navigation queued in the gap could consume a flag.
// UI-thread only.
std::multiset trusted_pending;
@@ -533,11 +539,12 @@ struct Slot {
uint32_t dialog_next = 1;
// Visibility (kOpSetVisible -> WasHidden). UI-thread only. On Windows there
- // is no begin-frame pump to gate (LAW 1); this drives WasHidden plus the
- // hidden->visible repaint kick (the F-1 keystone, main.mm:1962-1985).
+ // is no begin-frame pump to gate (no external begin frames); this drives
+ // WasHidden plus the hidden->visible repaint kick (as the macOS host's
+ // DoSetVisible in browser_ops.mm).
bool visible = true;
- // F-1/F-2: a dpr change landing while hidden defers its screen-info
- // re-assert to the hidden->visible edge. UI-thread only.
+ // A dpr change landing while hidden defers its screen-info re-assert to the
+ // hidden->visible edge, where the repaint kick picks it up. UI-thread only.
bool needs_screen_info_on_show = false;
// The JS channels this browser's consumer registered, before create (they
@@ -626,9 +633,10 @@ class HostRenderHandler : public CefRenderHandler {
}
// The REAL display (DIP), not the tile: `window.screen == innerWidth` is a
- // textbook headless/OSR fingerprint (see main.mm RealScreenDip:586-609 and
- // commit 855042d). GetSystemMetrics returns px in this process's DPI
- // context; divide by dpr for DIP. Falls back to a plausible frame.
+ // textbook headless/OSR fingerprint (see the macOS host's RealScreenDip in
+ // render_handler.mm and commit 855042d). GetSystemMetrics returns px in this
+ // process's DPI context; divide by dpr for DIP. Falls back to a plausible
+ // frame.
static void RealScreenDip(double dpr, CefRect& full, CefRect& work) {
const double s = dpr > 0.0 ? dpr : 1.0;
const int pw = GetSystemMetrics(SM_CXSCREEN);
@@ -647,7 +655,7 @@ class HostRenderHandler : public CefRenderHandler {
}
// Device scale so CEF renders logical*dpr (HiDPI-native) + real screen
- // bounds and color depth (mirrors main.mm GetScreenInfo:614-625).
+ // bounds and color depth (mirrors the macOS host's GetScreenInfo).
bool GetScreenInfo(CefRefPtr, CefScreenInfo& info) override {
std::lock_guard lock(slot_->surface_mutex);
info.device_scale_factor = static_cast(slot_->dpr);
@@ -662,8 +670,8 @@ class HostRenderHandler : public CefRenderHandler {
}
// Plausible window frame at a non-zero offset, taller than the view by
- // typical browser chrome (outerHeight > innerHeight like a real window) —
- // main.mm GetRootScreenRect:633-638.
+ // typical browser chrome (outerHeight > innerHeight like a real window), as
+ // the macOS host's GetRootScreenRect does.
bool GetRootScreenRect(CefRefPtr, CefRect& rect) override {
std::lock_guard lock(slot_->surface_mutex);
constexpr int kChromeH = 87;
@@ -695,7 +703,7 @@ class HostRenderHandler : public CefRenderHandler {
d.SampleDesc.Count = 1;
d.Usage = D3D11_USAGE_DEFAULT;
d.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
- d.MiscFlags = D3D11_RESOURCE_MISC_SHARED; // LEGACY handle (LAW 5)
+ d.MiscFlags = D3D11_RESOURCE_MISC_SHARED; // LEGACY handle, for ANGLE
ComPtr fresh;
HRESULT hr = g_d3d_device->CreateTexture2D(&d, nullptr, &fresh);
if (FAILED(hr)) {
@@ -713,7 +721,7 @@ class HostRenderHandler : public CefRenderHandler {
return false;
}
// Park the old bridge until the present carrying the NEW handle is sent
- // (belt-1 friendliness — the plugin's own opened ref is the primary belt).
+ // (a courtesy — the plugin's own opened ref is what keeps it alive).
slot_->retired_bridge = slot_->bridge;
slot_->bridge = fresh;
slot_->bridge_handle =
@@ -726,9 +734,9 @@ class HostRenderHandler : public CefRenderHandler {
}
// Present the just-filled bridge, tagging the frame with the bridge handle
- // (the identity, LAW 3) and the PHYSICAL px dims of the frame actually
- // composited (the size-gate signal, LAW 4 / PROTOCOL.md §5). Caller holds
- // slot_->surface_mutex. Windows payload (LAW 10):
+ // (the identity Flutter sees) and the PHYSICAL px dims of the frame actually
+ // composited (so the plugin's size-gate can refuse stale-size frames,
+ // PROTOCOL.md §5). Caller holds slot_->surface_mutex. Windows-only payload:
// {u64 bridgeHandle BE}{u32 srcW BE}{u32 srcH BE} = 16 bytes.
void SendPresentLocked(int srcW, int srcH) {
uint8_t p[16];
@@ -737,7 +745,7 @@ class HostRenderHandler : public CefRenderHandler {
WriteU32BE(p + 12, static_cast(srcH < 0 ? 0 : srcH));
SendFrame(slot_->browser_id, kOpPresent, p, 16);
// The present announcing the new bridge is on the wire — the old bridge
- // may now die (the plugin holds its own reference to it, LAW 6).
+ // may now die (the plugin holds its own reference to it).
slot_->retired_bridge.Reset();
}
@@ -795,7 +803,7 @@ class HostRenderHandler : public CefRenderHandler {
}
// Flush the device and check it survived; on loss reset it so the next paint
- // re-creates it and every bridge (#9). False = don't present this frame.
+ // re-creates it and every bridge. False = don't present this frame.
// Caller holds g_d3d_mutex.
bool FlushAndCheckDeviceLocked(const char* where) {
g_d3d_ctx->Flush();
@@ -822,11 +830,10 @@ class HostRenderHandler : public CefRenderHandler {
slot_->popup_rect = rect;
}
- // GPU pixel path (LAW 2, the S1 cef_leg.cpp recipe): CEF's GPU process
- // composites the page and hands an NT shared handle valid ONLY inside this
- // callback. Open it on our device, copy it into the legacy bridge (the view)
- // or the kept dropdown texture (PET_POPUP), Flush — all synchronously, never
- // storing the NT handle.
+ // GPU pixel path: CEF's GPU process composites the page and hands an NT
+ // shared handle valid ONLY inside this callback. Open it on our device, copy
+ // it into the legacy bridge (the view) or the kept dropdown texture
+ // (PET_POPUP), Flush — all synchronously, never storing the NT handle.
void OnAcceleratedPaint(CefRefPtr, PaintElementType type,
const RectList&,
const CefAcceleratedPaintInfo& info) override {
@@ -848,13 +855,13 @@ class HostRenderHandler : public CefRenderHandler {
int srcW = 0, srcH = 0;
{
std::lock_guard d3d(g_d3d_mutex);
- // LAW 2: open + copy INSIDE the callback. `src` (the ComPtr) is
- // released before we return; the NT handle itself is never kept.
+ // Open + copy INSIDE the callback (the NT handle dies with it). `src`
+ // (the ComPtr) is released before we return; the handle is never kept.
ComPtr src;
HRESULT hr = g_d3d_device1->OpenSharedResource1(
nt, __uuidof(ID3D11Texture2D), &src);
if (FAILED(hr)) {
- // #9: distinguish a transient bad-handle miss from real device loss. On
+ // Distinguish a transient bad-handle miss from real device loss. On
// loss, drop the cached device (+ bump the epoch) so the next paint
// re-creates the device and every bridge — otherwise the dead device is
// cached forever and the tile never repaints.
@@ -898,7 +905,7 @@ class HostRenderHandler : public CefRenderHandler {
CompositePopupLocked();
// CopyResource/Flush return void; device loss surfaces via
// GetDeviceRemovedReason. If it went down mid-blit, reset and skip this
- // frame — never present pixels from a dead device (#9).
+ // frame — never present pixels from a dead device.
if (!FlushAndCheckDeviceLocked("blit")) return;
}
}
@@ -1017,9 +1024,9 @@ std::string CrashBurstText() {
void DoShutdown(); // defined below; the crash-loop exit reuses it
-// Deny-default permission gate (verbatim port of main.mm:1068-1089): no
-// per-site UI exists here, so every permission prompt and media-access
-// request is denied up front.
+// Deny-default permission gate (verbatim port of the macOS host's
+// HostPermissionHandler in host_client.mm): no per-site UI exists here, so
+// every permission prompt and media-access request is denied up front.
class HostPermissionHandler : public CefPermissionHandler {
public:
bool OnRequestMediaAccessPermission(
@@ -1045,7 +1052,8 @@ class HostPermissionHandler : public CefPermissionHandler {
// it at 2 MB. So the plugin can hand us the HTML plus the URL it should appear
// to come from, and we answer the MAIN-FRAME request for exactly that URL with
// the HTML instead of the network. Everything else the page loads goes to the
-// network as normal. Verbatim port of main.mm's g_authored.
+// network as normal. Verbatim port of the macOS host's g_authored
+// (authored_content.mm).
//
// Keyed by WIRE ID and written on the reader thread, not stored on the Slot: the
// frame is sent immediately ahead of the create / load it belongs to and must be
@@ -1170,10 +1178,11 @@ class HostClient : public CefClient,
public:
explicit HostClient(std::shared_ptr slot) : slot_(std::move(slot)) {
// Browser-side message router (default config: window.cefQuery /
- // cefQueryCancel) — the SAME config the renderer half uses (main.mm:
- // 1228-1234). One router per HostClient, i.e. per browser: OnQuery below
- // stamps slot_->browser_id so a page message is delivered ONLY to the
- // originating session (per-session routing, channel_probe_shared).
+ // cefQueryCancel) — the SAME config the renderer half uses (as on macOS,
+ // host_client.mm + process_helper.mm). One router per HostClient, i.e. per
+ // browser: OnQuery below stamps slot_->browser_id so a page message is
+ // delivered ONLY to the originating session (per-session routing,
+ // channel_probe_shared).
CefMessageRouterConfig config;
router_ = CefMessageRouterBrowserSide::Create(config);
router_->AddHandler(this, false);
@@ -1223,7 +1232,7 @@ class HostClient : public CefClient,
return true;
}
- // CefFindHandler (main.mm:1260-1275).
+ // CefFindHandler (as on macOS).
void OnFindResult(CefRefPtr, int, int count, const CefRect&,
int activeMatchOrdinal, bool finalUpdate) override {
uint8_t p[9];
@@ -1233,7 +1242,7 @@ class HostClient : public CefClient,
SendFrame(slot_->browser_id, kOpFindResult, p, 9);
}
- // CefJSDialogHandler (main.mm:1279-1315): forward alert/confirm/prompt;
+ // CefJSDialogHandler (as on macOS): forward alert/confirm/prompt;
// the plugin answers via kOpJsDialogResp -> DoJsDialogResp -> Continue.
bool OnJSDialog(CefRefPtr, const CefString&,
JSDialogType dialog_type, const CefString& message_text,
@@ -1300,7 +1309,7 @@ class HostClient : public CefClient,
}
}
- // CefMessageRouter wiring (main.mm:1468-1501). The renderer half (HostApp
+ // CefMessageRouter wiring (as on macOS). The renderer half (HostApp
// below) injects window.cefQuery; queries land here. Forward the request to
// the plugin: "eval::" -> kOpEvalResult (a
// runJavaScriptReturningResult result); "ch::" ->
@@ -1352,7 +1361,7 @@ class HostClient : public CefClient,
}
// Route renderer->browser process messages through the message router
- // (main.mm:1495-1501). This carries the cefQuery payloads that surface in
+ // (as on macOS). This carries the cefQuery payloads that surface in
// OnQuery above.
bool OnProcessMessageReceived(CefRefPtr browser,
CefRefPtr frame,
@@ -1373,7 +1382,7 @@ class HostClient : public CefClient,
SendUtf8(slot_->browser_id, kOpPageStart,
policy::TruncateUtf8(frame->GetURL().ToString(),
policy::kMaxPagePayload));
- // SECURITY (main.mm:1339-1343): install the JS-channel shims ONLY into
+ // SECURITY (as on macOS): install the JS-channel shims ONLY into
// the MAIN frame — injecting the privileged window. bridge into a
// cross-origin subframe would hand an untrusted iframe that bridge.
for (const auto& name : slot_->channels) InjectChannelShim(frame, name);
@@ -1385,9 +1394,10 @@ class HostClient : public CefClient,
SendUtf8(slot_->browser_id, kOpPageFinish,
policy::TruncateUtf8(frame->GetURL().ToString(),
policy::kMaxPagePayload));
- // C1 render floor (main.mm:1350-1363, minus the external begin-frame
- // per LAW 1): re-assert size + damage when the main frame finishes so a
- // coalesced/dropped first frame is re-driven. Hidden tiles stay paused.
+ // Render floor (the macOS host's OnLoadEnd, minus the external
+ // begin-frame this host never sends): re-assert size + damage when the
+ // main frame finishes so a coalesced/dropped first frame is re-driven
+ // instead of leaving a blank tile. Hidden tiles stay paused.
if (browser && browser->GetHost() && slot_->visible) {
auto h = browser->GetHost();
h->WasResized();
@@ -1428,11 +1438,11 @@ class HostClient : public CefClient,
SendFrame(slot_->browser_id, kOpProgress, p, 4);
}
- // H3: the async create completes here. Bind the browser, ack kOpCreated,
+ // The async create completes here. Bind the browser, ack kOpCreated,
// honor a deferred close, then catch the browser up on everything that
// arrived while the create was in flight: visibility, a resize, and the ops
- // kept in slot_->deferred, in order. No begin-frame pump to start (LAW 1 —
- // CEF's internal frame timer drives paints).
+ // kept in slot_->deferred, in order. No begin-frame pump to start (CEF's
+ // internal frame timer drives paints).
void OnAfterCreated(CefRefPtr browser) override {
if (std::getenv("FLUTTER_CEF_DEBUG"))
LogErr("[cef_host] OnAfterCreated wire=%u", slot_->browser_id);
@@ -1469,12 +1479,13 @@ class HostClient : public CefClient,
}
}
- // Popups. macOS (main.mm:1444-1451) splits by disposition: a SIZED popup
- // (CEF_WOD_NEW_POPUP — the window.open-with-features shape OAuth/"Sign in with
- // Google" uses) gets a REAL native window so window.opener/postMessage/
- // window.close work; everything else (target=_blank / plain new tab) diverts
- // to kOpNewWindow and loads in-place. The native-window port is post-slice on
- // Windows, so sized popups still divert in-tab — but that CANNOT complete the
+ // Popups. macOS (OnBeforePopup in host_client.mm) splits by disposition: a
+ // SIZED popup (CEF_WOD_NEW_POPUP — the window.open-with-features shape
+ // OAuth/"Sign in with Google" uses) gets a REAL native window so
+ // window.opener/postMessage/window.close work; everything else
+ // (target=_blank / plain new tab) diverts to kOpNewWindow and loads
+ // in-place. The native popup window is not ported to Windows yet, so sized
+ // popups still divert in-tab — but that CANNOT complete the
// opener/postMessage handshake (it strands sign-in). Emit a distinct log per
// CEF_WOD_NEW_POPUP so the regression is visible, not silent.
bool OnBeforePopup(CefRefPtr, CefRefPtr, int,
@@ -1488,11 +1499,11 @@ class HostClient : public CefClient,
if (!logged.exchange(true))
SendLog(slot_->browser_id,
"OnBeforePopup: sized popup (CEF_WOD_NEW_POPUP) diverted in-tab "
- "— native OAuth-popup window is post-slice on Windows; "
+ "— Windows has no native OAuth-popup window yet, so "
"window.open sign-in (opener/postMessage) will not complete "
- "(macOS OpenNativeAuthPopup, main.mm:1444)");
+ "(macOS opens one: OpenNativeAuthPopup)");
}
- // Non-native case (matches macOS's non-popup branch, main.mm:1449-1451):
+ // Non-native case (matches macOS's non-popup branch):
// load the target in this tile.
if (!target_url.empty())
SendUtf8(slot_->browser_id, kOpNewWindow,
@@ -1510,9 +1521,9 @@ class HostClient : public CefClient,
return true;
}
- // Centralized per-browser teardown (main.mm:1510-1527): drop the routing
- // entry, release the bridge under the slot lock (closing set FIRST so a
- // racing paint can't re-mint), break the retain cycle.
+ // Centralized per-browser teardown (as the macOS OnBeforeClose): drop the
+ // routing entry, release the bridge under the slot lock (closing set FIRST so
+ // a racing paint can't re-mint), break the retain cycle.
void OnBeforeClose(CefRefPtr browser) override {
if (router_) router_->OnBeforeClose(browser);
SetAuthoredDoc(slot_->browser_id, "", "");
@@ -1533,7 +1544,7 @@ class HostClient : public CefClient,
}
// Ctrl-key editing shortcuts as the FALLBACK they are in a real browser
- // (main.mm OnKeyEvent). CefWebView sends Ctrl+C/X/V/A/Z/Y to the page as raw
+ // (as macOS OnKeyEvent). CefWebView sends Ctrl+C/X/V/A/Z/Y to the page as raw
// keys, so an editor that owns its undo stack and selection (Monaco) handles
// them in its keydown listener; Blink's own key bindings run the edit command
// when the page doesn't. OnKeyEvent is called only for a key both left
@@ -1575,13 +1586,13 @@ class HostClient : public CefClient,
return new AuthoredRequestHandler(std::move(html));
}
- // Navigation scheme allowlist (main.mm:1528-1565). Empty allowlist = allow
+ // Navigation scheme allowlist (as on macOS). Empty allowlist = allow
// all. Main-frame only; kOpLoadTrusted's exact-URL exemptions are consumed
// here.
bool OnBeforeBrowse(CefRefPtr browser, CefRefPtr frame,
CefRefPtr request, bool, bool) override {
// Clean up any pending message-router queries for the frame about to
- // navigate (main.mm:1563) — otherwise a channel query in flight across a
+ // navigate (as on macOS) — otherwise a channel query in flight across a
// navigation can misfire or leak its callback.
if (router_) router_->OnBeforeBrowse(browser, frame);
if (g_allowed_schemes.empty()) return false; // allow
@@ -1618,9 +1629,9 @@ class HostClient : public CefClient,
// The one CefApp, used for BOTH the browser process (CefInitialize) and every
// re-exec'd sub-process (CefExecuteProcess). CEF calls GetBrowserProcessHandler
// in the browser process and GetRenderProcessHandler in the render process, so
-// the SAME binary hosts both halves of CefMessageRouter (main.mm's split across
-// main.mm + process_helper.mm collapses here — Windows re-runs cef_host.exe as
-// the render subprocess, LAW 8). The renderer half owns a
+// the SAME binary hosts both halves of CefMessageRouter (the macOS split across
+// host_client.mm + the helper's process_helper.mm collapses here — Windows
+// re-runs cef_host.exe as the render subprocess). The renderer half owns a
// CefMessageRouterRendererSide with the DEFAULT config (must match the
// browser-side HostClient config); it injects window.cefQuery into every frame
// and relays cefQuery calls to the browser process.
@@ -1635,7 +1646,7 @@ class HostApp : public CefApp,
return this;
}
- // ---- Render-process half (process_helper.mm:24-57 counterpart) ----
+ // ---- Render-process half (the macOS HelperApp counterpart) ----
// Render-process-only callback; create the renderer-side router here with the
// default config (window.cefQuery / cefQueryCancel).
void OnWebKitInitialized() override {
@@ -1714,7 +1725,7 @@ class HostApp : public CefApp,
const CefString& process_type,
CefRefPtr command_line) override {
(void)process_type;
- // OSR establishment latency (main.mm:1578-1592): OSR views have no real
+ // OSR establishment latency (as on macOS): OSR views have no real
// OS window, so Chromium backgrounds their renderers during first load.
if (!std::getenv("FLUTTER_CEF_KEEP_BG_THROTTLE")) {
command_line->AppendSwitch("disable-renderer-backgrounding");
@@ -1728,19 +1739,19 @@ class HostApp : public CefApp,
command_line->AppendSwitch("disable-gpu-compositing");
}
// Verbose Chromium logging (browser + propagated to children) only when
- // explicitly debugging (macOS main.mm:1627-1631 pattern).
+ // explicitly debugging (the macOS host's pattern).
if (std::getenv("FLUTTER_CEF_DEBUG")) {
command_line->AppendSwitch("enable-logging");
command_line->AppendSwitchWithValue(
"log-file", TempDirUtf8() + "cef_host_chromium.log");
command_line->AppendSwitchWithValue("v", "1");
}
- // Agent-control CDP-over-pipe translation (S3, P9): the plugin passed the
+ // Agent-control CDP-over-pipe translation: the plugin passed the
// two inherited pipe HANDLE values as --cdp-io-pipes=,; turn
// them into Chromium's real switches. Browser process only (process_type
// empty) — the renderer/GPU children never get the debugging pipe (and never
- // inherited the handles). Mirrors macOS main.mm's --cdp-pipe injection.
- // (disable-blink-features=AutomationControlled is post-slice.)
+ // inherited the handles). Mirrors the macOS --cdp-pipe injection in
+ // main.mm. (disable-blink-features=AutomationControlled is not ported.)
if (process_type.empty() && !g_cdp_io_pipes.empty()) {
command_line->AppendSwitch("remote-debugging-pipe");
command_line->AppendSwitchWithValue("remote-debugging-io-pipes",
@@ -1749,9 +1760,9 @@ class HostApp : public CefApp,
}
// Announce readiness. Payload = {readyFlags, protocolVersion}
- // (main.mm:1664-1682). Bit0 (ad-hoc/mock-keychain build) is a macOS-only
- // concern — Windows sends 0. The plugin sends NOTHING until this arrives,
- // then refuses on protocolVersion skew.
+ // (as the macOS OnContextInitialized). Bit0 (ad-hoc/mock-keychain build) is
+ // a macOS-only concern — Windows sends 0. The plugin sends NOTHING until this
+ // arrives, then refuses on protocolVersion skew.
void OnContextInitialized() override {
CEF_REQUIRE_UI_THREAD();
if (std::getenv("FLUTTER_CEF_DEBUG"))
@@ -1787,15 +1798,15 @@ void DoCreateBrowser(std::shared_ptr slot, std::string url) {
}
CefWindowInfo window_info;
// The hidden per-process WS_POPUP window as the windowless parent so
- // dialogs/menus/IMM degrade gracefully (PLAN §4.3; null works too — S5).
+ // dialogs/menus/IMM degrade gracefully (null works too for WebAuthn).
window_info.SetAsWindowless(g_hidden_hwnd);
// GPU OSR: the GPU process composites and hands OnAcceleratedPaint an NT
- // shared handle (the S1 pixel path). Without GPU compositing Chromium falls
+ // shared handle (the GPU pixel path). Without GPU compositing Chromium falls
// back to OnPaint, which the render handler uploads into the same bridge.
window_info.shared_texture_enabled = true;
- // LAW 1: external_begin_frame_enabled stays FALSE (default). With the
- // external pump only the FIRST browser in the process ever paints (S4);
- // CEF's internal frame timer at windowless_frame_rate drives paints.
+ // external_begin_frame_enabled stays FALSE (default). With the external
+ // pump only the FIRST browser in the process ever paints; CEF's internal
+ // frame timer at windowless_frame_rate drives paints.
window_info.external_begin_frame_enabled = false;
CefBrowserSettings settings;
settings.windowless_frame_rate = 60;
@@ -1816,7 +1827,7 @@ void DoCreateBrowser(std::shared_ptr slot, std::string url) {
slot->trusted_pending.insert(NormalizeAuthoredUrl(url));
}
CefRefPtr client = new HostClient(slot);
- // H3: ASYNC create. OnAfterCreated binds the browser and acks kOpCreated.
+ // ASYNC create. OnAfterCreated binds the browser and acks kOpCreated.
// Document-start scripts + create-time JS channels ride into the renderer as
// the browser's extra_info (see document_start.h) — the only channel that is
// in place before the first document's scripts run.
@@ -1824,7 +1835,7 @@ void DoCreateBrowser(std::shared_ptr slot, std::string url) {
window_info, client, url, settings,
TakeDocumentStartExtraInfo(wire_id, &slot->channels), nullptr);
if (!dispatched) {
- // H7: reclaim the slot + tell the plugin.
+ // Dispatch failed: reclaim the slot + tell the plugin.
SendLog(wire_id, "createBrowser: CreateBrowser dispatch failed");
SendFrame(wire_id, kOpCreateFailed, nullptr, 0);
SetAuthoredDoc(wire_id, "", "");
@@ -1855,16 +1866,16 @@ void DoDisposeBrowser(uint32_t wire_id) {
if (slot->browser) {
slot->browser->GetHost()->CloseBrowser(true);
} else {
- slot->close_requested = true; // H3 deferred-close intent
+ slot->close_requested = true; // OnAfterCreated closes it on bind
slot->deferred.clear();
}
}
-// LAW 4: every WasResized discards CEF's frame pool; late frames at the old
+// Every WasResized discards CEF's frame pool; late frames at the old
// size still arrive and are refused by the plugin's size-gate (each present
// carries its truthful dims). Producer-allocates: the bridge re-mints on the
-// first NEW-size paint, not here (main.mm DoResize:1828-1874, with
-// Invalidate standing in for SendExternalBeginFrame per LAW 1).
+// first NEW-size paint, not here (the macOS DoResize in browser_ops.mm, with
+// Invalidate standing in for SendExternalBeginFrame).
void DoResize(const std::shared_ptr& slot, int w, int h, double dpr) {
if (w < 1 || w > 16384 || h < 1 || h > 16384) {
SendLog(slot->browser_id, "resize: out-of-range dims " + std::to_string(w) +
@@ -1887,7 +1898,7 @@ void DoResize(const std::shared_ptr& slot, int w, int h, double dpr) {
slot->browser->GetHost()->WasResized();
slot->browser->GetHost()->Invalidate(PET_VIEW);
} else {
- // F-2: hidden — no frame can result; defer the screen-info re-assert +
+ // Hidden — no frame can result; defer the screen-info re-assert +
// repaint to the hidden->visible edge (DoSetVisible).
if (dpr_changed) slot->needs_screen_info_on_show = true;
}
@@ -1925,9 +1936,9 @@ void DoEditCommand(Slot& slot, int command) {
default: break;
}
}
-// Off-screen render gating + the F-1 hidden->visible repaint keystone
-// (main.mm DoSetVisible:1962-1985, Invalidate in place of the external
-// begin-frame per LAW 1).
+// Off-screen render gating + the hidden->visible repaint kick that stops a
+// culled tile coming back blank (the macOS DoSetVisible in browser_ops.mm,
+// Invalidate in place of the external begin-frame).
void DoSetVisible(const std::shared_ptr& slot, bool visible) {
const bool was_visible = slot->visible;
slot->visible = visible;
@@ -1957,7 +1968,7 @@ void DoJsDialogResp(const std::shared_ptr& slot, uint32_t id, bool ok,
cb->Continue(ok, text);
}
-// runJavaScriptReturningResult (main.mm DoEvalReturning:2001-2018, verbatim).
+// runJavaScriptReturningResult (the macOS DoEvalReturning, verbatim).
// Evaluate the user expression and post its JSON result back through
// window.cefQuery (-> HostClient::OnQuery -> kOpEvalResult "id:json",
// correlated to the Dart Future). `code` is the trusted host's JS (same trust
@@ -1975,7 +1986,7 @@ void DoEvalReturning(Slot& slot, uint32_t id, const std::string& code) {
frame->ExecuteJavaScript(js, "", 0);
}
-// Registers a JS channel for one browser (UI thread; mirrors main.mm
+// Registers a JS channel for one browser (UI thread; mirrors the macOS
// DoAddChannel). The slot exists from its create frame on, before the browser
// binds; a channel registered by then rides into the page at load.
void DoAddChannel(const std::shared_ptr& slot, const std::string& name) {
@@ -1990,7 +2001,7 @@ void DoAddChannel(const std::shared_ptr& slot, const std::string& name) {
if (slot->browser) InjectChannelShim(slot->browser->GetMainFrame(), name);
}
-// ---- Cookies (global manager = the shared profile jar; main.mm:2040-2140) ----
+// ---- Cookies (global manager = the shared profile jar, as on macOS) ----
std::string JsonEscape(const std::string& s) {
std::string out;
@@ -2068,7 +2079,7 @@ const char* SameSiteToString(cef_cookie_same_site_t v) {
}
}
-// COOKIE VERBS TAKE A WIRE ID, NOT A SLOT (parity with macOS main.mm). The jar is
+// COOKIE VERBS TAKE A WIRE ID, NOT A SLOT (parity with macOS). The jar is
// process-global, so nothing here needs the browser — the id only routes the
// reply/log. Requiring a live slot on the reader thread silently DROPPED any cookie
// verb that raced the create (the slot is registered by a later TID_UI task): a
@@ -2123,7 +2134,7 @@ void DoShowDevTools(Slot& slot) {
CefPoint());
}
-// ---- IME (main.mm:2219-2248) ----
+// ---- IME (as on macOS) ----
void DoImeSetComposition(Slot& slot, const std::string& text) {
CefString t(text);
uint32_t len = static_cast(t.length());
@@ -2143,7 +2154,7 @@ void DoImeSetComposition(Slot& slot, const std::string& text) {
}
// type: 0=move 1=down 2=up 3=wheel 4=leave; button: 0=left 1=middle 2=right.
-// x/y logical (DIP) view coords, exactly like macOS (main.mm:2251-2285).
+// x/y logical (DIP) view coords, exactly like macOS.
void DoPointer(Slot& slot, int type, int button, int click_count,
uint32_t modifiers, double x, double y, double dx, double dy) {
CefMouseEvent ev;
@@ -2180,7 +2191,7 @@ void DoPointer(Slot& slot, int type, int button, int click_count,
// type: 0=rawkeydown 2=keyup 3=char (cef_key_event_type_t). The Dart side
// sends REAL Windows virtual-key codes in windowsKeyCode on Windows (and the
// Unicode codepoint for char events) — native CefKeyEvent semantics, no
-// translation needed (main.mm DoKey:2288-2305; character fields always set
+// translation needed (as the macOS DoKey; character fields always set
// per the CEF t=11650 de-dup note, harmless on Windows).
void DoKey(Slot& slot, int type, uint32_t modifiers, int32_t windows_key_code,
int32_t native_key_code, uint32_t character) {
@@ -2195,7 +2206,8 @@ void DoKey(Slot& slot, int type, uint32_t modifiers, int32_t windows_key_code,
slot.browser->GetHost()->SendKeyEvent(ev);
}
-// C1: watchdog repaint re-kick (main.mm DoInvalidate:2310-2318). LAW 1: no
+// Force a repaint: the plugin's first-present watchdog sends kOpInvalidate
+// when a browser's first frame never arrived (the macOS DoInvalidate). No
// SendExternalBeginFrame — the internal frame timer honors Invalidate.
void DoInvalidate(const std::shared_ptr& slot) {
CEF_REQUIRE_UI_THREAD();
@@ -2223,7 +2235,7 @@ class ShutdownFlushCallback : public CefCompletionCallback {
// Tear down the WHOLE process (kOpShutdown / pipe EOF): close every browser,
// flush the cookie jar to disk, then quit the message loop. Per-slot cleanup
-// runs in OnBeforeClose (main.mm DoShutdown:2324-2335).
+// runs in OnBeforeClose (as the macOS DoShutdown in host_state.mm).
//
// WINDOWS COOKIE DURABILITY (no macOS analogue): unlike macOS — where the
// implicit CefShutdown flush reliably persists the jar — a fast Windows teardown
@@ -2234,7 +2246,7 @@ class ShutdownFlushCallback : public CefCompletionCallback {
// flushed, so we FlushStore here and defer the quit into its completion — that is
// what makes "stay signed in" survive relaunch. A bounded fallback quits anyway
// if the callback never fires, so teardown can't wedge inside the plugin reaper's
-// grace. macOS main.mm is unchanged (it does not need this).
+// grace. The macOS host does not need this.
void DoShutdown() {
CEF_REQUIRE_UI_THREAD();
std::vector> slots;
@@ -2257,10 +2269,10 @@ void DoShutdown() {
}
}
-// Reader thread: decode frames, marshal onto the CEF UI thread (mirrors
-// main.mm IpcReadLoop; payload layouts PROTOCOL.md §2). Per-browser ops go
-// through PostBrowserOp, which holds an op that beats the browser's bind until
-// OnAfterCreated instead of dropping it.
+// Reader thread: decode frames, marshal onto the CEF UI thread (mirrors the
+// macOS IpcReadLoop in ipc_reader.mm; payload layouts PROTOCOL.md §2).
+// Per-browser ops go through PostBrowserOp, which holds an op that beats the
+// browser's bind until OnAfterCreated instead of dropping it.
void IpcReadLoop() {
HANDLE pipe = g_ipc_pipe.load();
for (;;) {
@@ -2592,8 +2604,8 @@ void IpcReadLoop() {
}
// Create the ONE hidden window this host passes to SetAsWindowless(parent).
-// WS_POPUP, never shown. (PLAN §4.3: a null parent degrades dialogs/menus/
-// IMM; browser_platform_delegate_native_win.cc bails on !msg.hwnd.)
+// WS_POPUP, never shown. (A null parent degrades dialogs/menus/IMM;
+// browser_platform_delegate_native_win.cc bails on !msg.hwnd.)
HWND CreateHiddenHostWindow() {
WNDCLASSW wc = {};
wc.lpfnWndProc = DefWindowProcW;
@@ -2608,7 +2620,7 @@ HWND CreateHiddenHostWindow() {
// Entry point, invoked by CEF's bootstrap (bootstrapc.exe renamed to
// cef_host.exe). sandbox_info is forwarded to both CefExecuteProcess and
-// CefInitialize (SPIKES.md S2).
+// CefInitialize, so the child processes run sandboxed.
extern "C" CEF_BOOTSTRAP_EXPORT int RunConsoleMain(
int argc,
char* argv[],
@@ -2632,7 +2644,7 @@ extern "C" CEF_BOOTSTRAP_EXPORT int RunConsoleMain(
std::string profile_dir = GetSwitch(args, "--profile-dir=");
std::string allowed = GetSwitch(args, "--allowed-schemes=");
bool ephemeral = HasFlag(args, "--ephemeral");
- // Agent control (P9): "," inherited-HANDLE values. Stored in the
+ // Agent control: "," inherited-HANDLE values. Stored in the
// file-global so OnBeforeCommandLineProcessing (called from CefInitialize
// below) can inject the Chromium CDP-pipe switches. Empty when off.
g_cdp_io_pipes = GetSwitch(args, "--cdp-io-pipes=");
@@ -2672,7 +2684,7 @@ extern "C" CEF_BOOTSTRAP_EXPORT int RunConsoleMain(
}
// Profile dir fallback: a per-pid ephemeral temp dir (defensive — the
- // plugin always supplies --profile-dir, mirroring main.mm:34-35).
+ // plugin always supplies --profile-dir, mirroring the macOS main()).
if (profile_dir.empty()) {
profile_dir = TempDirUtf8() + "flutter_cef_ephem_" +
std::to_string(GetCurrentProcessId());
@@ -2680,8 +2692,8 @@ extern "C" CEF_BOOTSTRAP_EXPORT int RunConsoleMain(
}
CreateDirectoryW(Widen(profile_dir).c_str(), nullptr); // ok if it exists
- // Cross-process single-writer lock on a PERSISTENT profile dir (C2,
- // main.mm:2786-2806): exclusive-open /.flutter_cef.lock; on
+ // Cross-process single-writer lock on a PERSISTENT profile dir (as the
+ // macOS main()): exclusive-open /.flutter_cef.lock; on
// contention emit the machine-parseable kOpLog "profile-locked" and exit 2
// (the plugin keys processGone("locked") on that pair). The handle is held
// for the process lifetime — the OS drops it on exit. Ephemeral dirs are
@@ -2730,11 +2742,12 @@ extern "C" CEF_BOOTSTRAP_EXPORT int RunConsoleMain(
// process is what makes login shared across the tiles on a profile
// (CefCookieManager::GetGlobalManager -> this jar). persist_session_cookies
// keeps session cookies across relaunch — set UNCONDITIONALLY, mirroring
- // main.mm:2869 (harmless for an ephemeral host, whose dir the plugin's reaper
- // deletes on teardown; required for a named profile's "stay signed in").
+ // the macOS main() (harmless for an ephemeral host, whose dir the plugin's
+ // reaper deletes on teardown; required for a named profile's "stay signed
+ // in").
//
- // AT-REST (SPIKES.md S2 / §7): Windows OSCrypt encrypts the cookie/login
- // stores with DPAPI, which is ALWAYS available and signing-INDEPENDENT — so a
+ // AT-REST: Windows OSCrypt encrypts the cookie/login stores with DPAPI,
+ // which is ALWAYS available and signing-INDEPENDENT — so a
// named profile persists directly here, with NO analogue of the macOS ad-hoc
// "mock-keychain -> downgrade named profile to ephemeral" rule (there is no
// readyFlags bit0 gate on Windows; OnContextInitialized sends 0). DPAPI is
@@ -2748,11 +2761,11 @@ extern "C" CEF_BOOTSTRAP_EXPORT int RunConsoleMain(
else
settings.log_severity = LOGSEVERITY_ERROR;
- // no_sandbox and the sandbox_info passed here must agree (the S2-proven
- // pair). Passing the real sandbox_info while no_sandbox=1 makes every child
- // fail its mojo handshake ("Terminating current process after 15 seconds
- // with no connection"), so the escape hatch passes nullptr. sandbox_info is
- // always forwarded to CefExecuteProcess above: children must see it.
+ // no_sandbox and the sandbox_info passed here must agree. Passing the real
+ // sandbox_info while no_sandbox=1 makes every child fail its mojo handshake
+ // ("Terminating current process after 15 seconds with no connection"), so
+ // the escape hatch passes nullptr. sandbox_info is always forwarded to
+ // CefExecuteProcess above: children must see it.
if (sandbox_info != nullptr && !sandboxed)
LogErr("[cef_host] FLUTTER_CEF_NO_SANDBOX=1: running without the sandbox");
if (!CefInitialize(main_args, settings, app,
@@ -2762,7 +2775,7 @@ extern "C" CEF_BOOTSTRAP_EXPORT int RunConsoleMain(
return 10;
}
- // Reader thread (same model as macOS main.mm: reader posts to TID_UI via
+ // Reader thread (same model as the macOS host: reader posts to TID_UI via
// CefPostTask; CefRunMessageLoop owns the main thread).
std::thread reader;
if (g_ipc_pipe.load() != INVALID_HANDLE_VALUE) reader = std::thread(IpcReadLoop);
@@ -2786,9 +2799,9 @@ extern "C" CEF_BOOTSTRAP_EXPORT int RunConsoleMain(
// Teardown: invalidate the pipe FIRST (atomic exchange), THEN close — so a
// late SendFrame from a CEF thread can't write into a recycled handle
- // (mirrors main.mm:2910-2918 C3 ordering). Closing the pipe also unblocks
- // the reader's ReadFile, bounding the join... except a reader blocked in
- // ReadFile on a still-open pipe: cancel it explicitly first.
+ // (mirrors the macOS host's teardown ordering). Closing the pipe also
+ // unblocks the reader's ReadFile, bounding the join... except a reader
+ // blocked in ReadFile on a still-open pipe: cancel it explicitly first.
{
std::lock_guard lock(g_ipc_write_mutex);
HANDLE old = g_ipc_pipe.exchange(INVALID_HANDLE_VALUE);
diff --git a/packages/flutter_cef_windows/native/cef_host/fetch_cef.ps1 b/packages/flutter_cef_windows/native/cef_host/fetch_cef.ps1
index e7438e8..3b1a9f7 100644
--- a/packages/flutter_cef_windows/native/cef_host/fetch_cef.ps1
+++ b/packages/flutter_cef_windows/native/cef_host/fetch_cef.ps1
@@ -8,7 +8,7 @@
# %LOCALAPPDATA%/flutter_cef/. If neither exists, download the pinned
# tarball from cef-builds.spotifycdn.com, verify it against the SHA-256 pinned
# in cef_pin.txt (fail closed), extract with native tar.exe (bsdtar handles
-# .tar.bz2, docs/history/windows-port/SPIKES.md S6), and cache it under
+# .tar.bz2), and cache it under
# %LOCALAPPDATA%/flutter_cef for later builds. The CMake side checks the
# resolved tree's include/cef_version.h against the same pin.
@@ -78,8 +78,7 @@ Info "fetch_cef: extracting"
# Use the Windows system bsdtar by FULL PATH. A bare `tar` on a CI runner
# resolves to Git's bundled MSYS GNU tar, which reads "C:\...tarball" as a
# host:path remote spec ("Cannot connect to C:"). System32\tar.exe is libarchive
-# (bsdtar), handles .tar.bz2 and drive-letter paths natively
-# (docs/history/windows-port/SPIKES.md S6).
+# (bsdtar), handles .tar.bz2 and drive-letter paths natively.
$SystemTar = Join-Path $env:SystemRoot 'System32\tar.exe'
if (-not (Test-Path $SystemTar)) { $SystemTar = 'tar.exe' }
& $SystemTar -xf $tarball -C $tmp
diff --git a/packages/flutter_cef_windows/native/cef_host/test/pipe_probe.cc b/packages/flutter_cef_windows/native/cef_host/test/pipe_probe.cc
index 6bcf8ad..f28fc8b 100644
--- a/packages/flutter_cef_windows/native/cef_host/test/pipe_probe.cc
+++ b/packages/flutter_cef_windows/native/cef_host/test/pipe_probe.cc
@@ -1,8 +1,8 @@
// pipe_probe — standalone gate test for the Windows cef_host.
//
// Acts as the PLUGIN side of the IPC contract (PROTOCOL.md): creates the
-// named pipe server, spawns cef_host.exe against it, then drives the slice
-// vertical: kOpReady (version handshake) -> kOpCreateBrowser 1024x768@1.0 ->
+// named pipe server, spawns cef_host.exe against it, then drives one browser
+// end to end: kOpReady (version handshake) -> kOpCreateBrowser 1024x768@1.0 ->
// kOpCreated -> first kOpPresent within 20s -> kOpNavigate to a second URL ->
// url/title/loadState events -> kOpShutdown -> clean host exit.
//
diff --git a/packages/flutter_cef_windows/windows/CMakeLists.txt b/packages/flutter_cef_windows/windows/CMakeLists.txt
index 330c249..f2c4fa7 100644
--- a/packages/flutter_cef_windows/windows/CMakeLists.txt
+++ b/packages/flutter_cef_windows/windows/CMakeLists.txt
@@ -1,6 +1,6 @@
# flutter_cef_windows plugin build.
#
-# S6a/S6b findings baked in (specs/windows-port/SPIKES.md):
+# Toolchain findings from the Windows port, baked in:
# - keep apply_standard_settings (CEF headers are NOT linked here — the
# plugin talks to cef_host over IPC; CEF lives only in native/cef_host);
# - NOMINMAX per target (any TU that ever includes cef_types_win.h needs it,
@@ -49,7 +49,7 @@ apply_standard_settings(${PLUGIN_NAME})
set_target_properties(${PLUGIN_NAME} PROPERTIES
CXX_VISIBILITY_PRESET hidden)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
-# S6a: NOMINMAX everywhere windows.h is reachable.
+# NOMINMAX everywhere windows.h is reachable.
target_compile_definitions(${PLUGIN_NAME} PRIVATE NOMINMAX)
target_include_directories(${PLUGIN_NAME} INTERFACE
@@ -62,7 +62,7 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE
flutter_wrapper_plugin
d3d11
dxgi
- # Agent-control CDP relay (P9): winsock (loopback HTTP+WS server) + bcrypt
+ # Agent-control CDP relay: winsock (loopback HTTP+WS server) + bcrypt
# (SHA-1 Sec-WebSocket-Accept + BCryptGenRandom token). cdp_relay.cpp.
ws2_32
bcrypt
@@ -121,7 +121,7 @@ add_dependencies(${PLUGIN_NAME} flutter_cef_windows_host)
# Files the app needs beside its exe: the host pair + the CEF runtime.
# (Flat CEF layout on Windows; libcef.dll etc. resolve from the exe dir.
-# Set from what cef_leg (S1) needed to run windowless+GPU.)
+# The set a windowless, GPU-composited CEF needs at run time.)
set(flutter_cef_windows_bundled_libraries
"${CEF_HOST_DIST_DIR}/cef_host.exe"
"${CEF_HOST_DIST_DIR}/cef_host.dll"
@@ -149,8 +149,8 @@ set(flutter_cef_windows_bundled_libraries
# DESTINATION would resolve against CMAKE_INSTALL_PREFIX, whose genex is
# NOT re-evaluated inside this subdirectory's install script). BINARY_NAME
# is set by the app-level CMakeLists before generated_plugins.cmake runs.
-# TODO(builder-plugin): ship the full locale set (or make it configurable)
-# once the slice is green — en-US is the required minimum.
+# TODO(builder-plugin): ship the full locale set (or make it configurable);
+# en-US is the required minimum.
install(FILES "${FLUTTER_CEF_CEF_ROOT}/Resources/locales/en-US.pak"
DESTINATION "$/locales"
COMPONENT Runtime)
diff --git a/packages/flutter_cef_windows/windows/cdp_relay.cpp b/packages/flutter_cef_windows/windows/cdp_relay.cpp
index 803bf25..7cb6034 100644
--- a/packages/flutter_cef_windows/windows/cdp_relay.cpp
+++ b/packages/flutter_cef_windows/windows/cdp_relay.cpp
@@ -160,8 +160,8 @@ bool CdpRelay::Start() {
DLog("socket() failed");
return false;
}
- // SO_EXCLUSIVEADDRUSE, NOT SO_REUSEADDR (PLAN §4.5): on Windows SO_REUSEADDR
- // is a port-HIJACK primitive — it lets another same-user socket bind our
+ // SO_EXCLUSIVEADDRUSE, NOT SO_REUSEADDR: on Windows SO_REUSEADDR is a
+ // port-HIJACK primitive — it lets another same-user socket bind our
// already-bound loopback port and intercept the client's next connection
// (stealing the bearer token off the upgrade). Exclusive-use forbids that.
BOOL on = TRUE;
@@ -537,9 +537,9 @@ void CdpRelay::FrameLoop(SOCKET fd) {
assembling = !fin;
if (fin) {
if (assembling_text) {
- // Single-tile passthrough: forward the CDP message verbatim. (The
- // CEF-2b filterClientToPipe + rewriteOutgoingId hooks would go here
- // for the N-tile follow-up — see the header comment.)
+ // Single-tile passthrough: forward the CDP message verbatim.
+ // (CdpRelay.swift's filterClientToPipe + rewriteOutgoingId would
+ // go here for the N-tile follow-up — see the header comment.)
send_to_pipe_(std::string(msg.begin(), msg.end()));
}
msg.clear();
@@ -567,8 +567,8 @@ void CdpRelay::FrameLoop(SOCKET fd) {
}
void CdpRelay::DeliverToClient(const std::string& json) {
- // Single-tile passthrough: deliver verbatim. (The CEF-2b demux + filter
- // seam — demuxPipeToClient/filterPipeToClient — would gate this for N-tile.)
+ // Single-tile passthrough: deliver verbatim. (CdpRelay.swift's per-tile
+ // demuxPipeToClient/filterPipeToClient would gate this for N-tile.)
std::vector payload(json.begin(), json.end());
std::lock_guard lock(client_lock_);
if (client_sock_ == INVALID_SOCKET) return;
diff --git a/packages/flutter_cef_windows/windows/cdp_relay.h b/packages/flutter_cef_windows/windows/cdp_relay.h
index c1cd090..2483e2e 100644
--- a/packages/flutter_cef_windows/windows/cdp_relay.h
+++ b/packages/flutter_cef_windows/windows/cdp_relay.h
@@ -4,7 +4,7 @@
// packages/flutter_cef_macos/macos/Classes/CdpRelay.swift (the canonical
// relay): it re-exposes cef_host's CDP-over-pipe (Chromium
// --remote-debugging-pipe + --remote-debugging-io-pipes, NUL-framed JSON on
-// two inherited anonymous pipes — the S3 recipe) to a standard CDP client
+// two inherited anonymous pipes) to a standard CDP client
// (Playwright via connectOverCDP / agent-browser) as a loopback HTTP+WebSocket
// endpoint. The Swift protocol logic ports directly: the POSIX socket calls
// (socket/bind/listen/accept/recv/send, fd) become winsock (WSAStartup,
@@ -24,11 +24,11 @@
// - Single active client — a second concurrent ws upgrade is rejected 503.
// - CSPRNG token (BCryptGenRandom), constant-time compared.
//
-// SINGLE-TILE SCOPE (P9): this relay is the raw browser-level PASSTHROUGH
-// (CEF-2a) — the CDP pipe carries exactly ONE page target (the one tile), so a
-// passthrough is functionally correct and safe (there is no sibling tile to
-// hide). The per-tile Target-domain FILTER + N-relay CDP-id MULTIPLEX (CEF-2b —
-// the `scopeTargetId`/`relayId` machinery in CdpRelay.swift:560-884) is the
+// SINGLE-TILE SCOPE: this relay is the raw browser-level PASSTHROUGH — the
+// CDP pipe carries exactly ONE page target (the one tile), so a passthrough is
+// functionally correct and safe (there is no sibling tile to hide). The
+// per-tile Target-domain FILTER + N-relay CDP-id MULTIPLEX (the
+// `scopeTargetId` filter and `CdpPipeIds` remap in CdpRelay.swift) is the
// documented follow-up for N-tile Target multiplexing and is NOT implemented
// here. The `scope_target_id` seam below is left in place for it (empty =
// passthrough today); see the class comment where the filter hooks would go.
@@ -64,10 +64,10 @@ class CdpRelay : public std::enable_shared_from_this {
// safe to call from the relay's frame-loop thread.
using SendToPipe = std::function;
- // `send_to_pipe` bridges client->pipe. `scope_target_id` is the CEF-2b
- // per-tile filter seam: empty (the P9 single-tile default) is the raw
- // browser-level passthrough; a non-empty targetId would engage the
- // Target-domain filter (NOT implemented in the slice — see the file comment).
+ // `send_to_pipe` bridges client->pipe. `scope_target_id` is the per-tile
+ // filter seam: empty (the single-tile default) is the raw browser-level
+ // passthrough; a non-empty targetId would engage the Target-domain filter
+ // (NOT implemented yet — see the file comment).
explicit CdpRelay(SendToPipe send_to_pipe,
std::string scope_target_id = std::string());
~CdpRelay();
@@ -120,7 +120,7 @@ class CdpRelay : public std::enable_shared_from_this {
void WriteRaw(SOCKET fd, const std::string& s);
const SendToPipe send_to_pipe_;
- const std::string scope_target_id_; // CEF-2b seam (empty = passthrough)
+ const std::string scope_target_id_; // per-tile seam (empty = passthrough)
std::string token_;
uint16_t port_ = 0;
diff --git a/packages/flutter_cef_windows/windows/flutter_cef_plugin.cpp b/packages/flutter_cef_windows/windows/flutter_cef_plugin.cpp
index aad22c9..f0f8561 100644
--- a/packages/flutter_cef_windows/windows/flutter_cef_plugin.cpp
+++ b/packages/flutter_cef_windows/windows/flutter_cef_plugin.cpp
@@ -126,7 +126,7 @@ std::vector GetStringList(const flutter::EncodableMap& m,
return out;
}
-// A `profile` arg is "named" only when present AND non-empty (Swift:288-289).
+// A `profile` arg is "named" only when present AND non-empty (as on macOS).
bool HasNamedProfile(const flutter::EncodableMap& m) {
auto it = m.find(flutter::EncodableValue(std::string("profile")));
if (it == m.end()) return false;
@@ -197,10 +197,10 @@ flutter::EncodableValue Ev(const char* s) {
}
// Sanitize a profile name to a filesystem-safe leaf: anything outside
-// [A-Za-z0-9._-] -> '_' (macOS resolveProfileDir, FlutterCefPlugin.swift:
-// 710-717). A leaf of all dots ("."/".."/"...") would resolve to the
-// profiles/ container or its PARENT — a one-level containment escape whose
-// protected-DACL apply would clobber a shared ancestor — so neutralize it.
+// [A-Za-z0-9._-] -> '_' (as macOS resolveProfileDir does). A leaf of all dots
+// ("."/".."/"...") would resolve to the profiles/ container or its PARENT — a
+// one-level containment escape whose protected-DACL apply would clobber a
+// shared ancestor — so neutralize it.
std::wstring SanitizeProfileLeaf(const std::string& profile) {
std::wstring out;
out.reserve(profile.size());
@@ -265,8 +265,8 @@ void FlutterCefPlugin::RegisterWithRegistrar(
FlutterCefPlugin::FlutterCefPlugin(flutter::PluginRegistrarWindows* registrar)
: registrar_(registrar) {
// Reclaim ephemeral profile dirs orphaned by a previous crash/kill before we
- // start minting new ones (macOS sweepStaleEphemeralProfiles at plugin init,
- // FlutterCefPlugin.swift:97-109). No host is live yet.
+ // start minting new ones (as macOS sweepStaleEphemeralProfiles does at plugin
+ // init). No host is live yet.
SweepStaleEphemeralProfiles();
channel_ = std::make_unique>(
registrar->messenger(), "flutter_cef",
@@ -479,7 +479,7 @@ void FlutterCefPlugin::HandleMethodCall(
}
if (method == "pointer") {
// {u8 type}{u8 button}{u8 clickCount}{u8 pad}{u32 modifiers}
- // {f64 x}{f64 y}{f64 dx}{f64 dy} = 40 bytes (main.mm:2560-2570).
+ // {f64 x}{f64 y}{f64 dx}{f64 dy} = 40 bytes.
if (s) {
std::vector p;
p.reserve(40);
@@ -519,9 +519,9 @@ void FlutterCefPlugin::HandleMethodCall(
if (s) {
const bool visible = GetBool(args, "visible", true);
SendOrQueue(s, kOpSetVisible, {visible ? uint8_t{1} : uint8_t{0}});
- // C1 watchdog: a hidden CEF browser produces no frames by design, so
- // suspend the first-present watchdog while hidden and re-arm it on show
- // if still blank (macOS noteVisibility, CefProfileHost.swift:706-729).
+ // First-present watchdog: a hidden CEF browser produces no frames by
+ // design, so suspend the watchdog while hidden and re-arm it on show if
+ // still blank (as macOS CefProfileHost.noteVisibility does).
s->visible = visible;
if (!visible) {
CancelWatchdog(s);
@@ -614,7 +614,7 @@ void FlutterCefPlugin::HandleMethodCall(
result->Success();
return;
}
- // ---- Cookies (shared per-profile jar; verbs match main.mm byte-for-byte).
+ // ---- Cookies (shared per-profile jar; verbs match the macOS host exactly).
// The four verbs operate on the process-wide CefCookieManager inside the
// host, so on a shared persistent profile every session sees one jar (macOS
// parity). getCookies (Dart) rides the visitCookies verb, correlated back by
@@ -622,7 +622,7 @@ void FlutterCefPlugin::HandleMethodCall(
if (method == "setCookie") {
if (s) {
// {utf8 url\0name\0value\0domain\0path\0secure\0httpOnly\0sameSite}
- // (main.mm kOpSetCookie). Hosts older than the attribute fields read
+ // (kOpSetCookie). Hosts older than the attribute fields read
// only the first five.
const std::string payload = GetString(args, "url") + '\0' +
GetString(args, "name") + '\0' +
@@ -648,7 +648,7 @@ void FlutterCefPlugin::HandleMethodCall(
}
if (method == "visitCookies") {
if (s) {
- // {u32 id}{utf8 url} — url empty = all (main.mm kOpVisitCookies:154).
+ // {u32 id}{utf8 url} — url empty = all.
std::vector p;
AppendU32(p, static_cast(GetInt(args, "id", 0)));
AppendUtf8(p, GetString(args, "url"));
@@ -659,7 +659,7 @@ void FlutterCefPlugin::HandleMethodCall(
}
if (method == "deleteCookie") {
if (s) {
- // {utf8 url\0name} (main.mm kOpDeleteCookie:155).
+ // {utf8 url\0name}.
const std::string payload =
GetString(args, "url") + '\0' + GetString(args, "name");
std::vector p(payload.begin(), payload.end());
@@ -795,7 +795,7 @@ void FlutterCefPlugin::HandleCreate(
spec.profile = GetString(args, "profile");
spec.host_group = GetString(args, "hostGroup");
// Agent control: CDP-over-pipe launch. Off by default; when set, the host
- // is spawned with the two inherited CDP pipes (the S3 recipe). Independent
+ // is spawned with the two inherited CDP pipes. Independent
// of enableCdp (TCP) — the pipe path never opens a listening port.
spec.agent_control = GetBool(args, "agentControl", false);
spec.channels = GetStringList(args, "channels");
@@ -1069,7 +1069,7 @@ FlutterCefPlugin::Host* FlutterCefPlugin::ResolveOrSpawnHost(
const uint64_t gen = host->generation;
// Reader thread: frames + EOF, posted to the platform thread. host_key + gen
// are captured by value so a stale OLD-host event posted during the reaper
- // grace of a same-profile respawn is dropped on drain (C1).
+ // grace of a same-profile respawn is dropped on drain.
host->pipe->StartReader(
[this, host_key, gen](uint32_t browser_id, uint8_t opcode,
std::vector payload) {
@@ -1404,7 +1404,7 @@ void FlutterCefPlugin::SendToHost(Host* host, uint32_t browser_id,
PostEvent(std::move(e));
}
-// ---- agent control (P9) ----
+// ---- agent control ----
// static
void FlutterCefPlugin::CdpReadLoop(std::shared_ptr transport) {
@@ -1465,7 +1465,7 @@ void FlutterCefPlugin::EnableAgentControl(
"agentControl: true");
return;
}
- // SECURITY (P9 single-tile invariant, see HandleCreate): the browser-level
+ // SECURITY (single-tile invariant, see HandleCreate): the browser-level
// relay would expose every sibling tile on this host to the agent. Until the
// per-tile Target filter lands, refuse a grant while the host serves >1 tile.
if (h->browsers.size() > 1) {
@@ -1593,8 +1593,8 @@ void FlutterCefPlugin::HandleHostFrame(const std::string& host_key,
const uint8_t host_version = payload.size() >= 2 ? payload[1] : 0;
if (host_version != kCefHostProtocolVersion) {
// Nothing flushed yet, so nothing mis-parsed. No auto-respawn (it would
- // re-resolve the same binary and loop) — fail every session (Swift:
- // 528-533).
+ // re-resolve the same binary and loop) — fail every session, as macOS
+ // does in its onProtocolMismatch handler.
std::ostringstream reason;
reason << "protocolMismatch(host=v" << static_cast(host_version)
<< ")";
@@ -1687,7 +1687,7 @@ void FlutterCefPlugin::HandleSessionFrame(
break;
case kOpLoadErr: {
if (payload.size() < 4) break;
- // {u32 code}{utf8 "url\ntext"} — split at the FIRST '\n' (Swift:374-378).
+ // {u32 code}{utf8 "url\ntext"} — split at the FIRST '\n', as on macOS.
const std::string combined = PayloadString(payload, 4);
const size_t nl = combined.find('\n');
const std::string err_url =
@@ -1808,7 +1808,7 @@ void FlutterCefPlugin::HandleSessionFrame(
break;
case kOpCookies:
// {u32 id}{utf8 json-array} — correlated back to the Dart getCookies
- // Future by request id (main.mm kOpCookies:130; Swift onCookies:422-424).
+ // Future by request id (macOS: CefWebSession.onCookies).
if (payload.size() >= 4) {
EmitEvent("cookies", session_id,
{{Ev("id"), flutter::EncodableValue(static_cast(
@@ -1828,9 +1828,9 @@ void FlutterCefPlugin::HandleSessionFrame(
if (!s->visible) SendOrQueue(s, kOpSetVisible, {uint8_t{0}});
break;
case kOpCreateFailed: {
- // H7: this browser's create failed; the host process is otherwise
- // healthy — drop just this ONE session (emit processGone, close its
- // browser, keep the host for its siblings; Swift:537-549). Copy the id
+ // This browser's create failed; the host process is otherwise healthy —
+ // drop just this ONE session (emit processGone, close its browser, keep
+ // the host for its siblings, as macOS onBrowserGone does). Copy the id
// first: DisposeSession frees `s`, dangling session_id (an alias of
// s->id).
const std::string sid = session_id;
@@ -1948,11 +1948,11 @@ std::wstring FlutterCefPlugin::MakeEphemeralProfileDir() {
std::wstring FlutterCefPlugin::MakePersistentProfileDir(
const std::string& profile) {
// %LOCALAPPDATA%\flutter_cef\profiles\, created with a
- // current-user-SID protected DACL (the #3 pipe hardening pattern applied to
- // the profile tree). Mirrors macOS resolveProfileDir's stable 0700 dir under
- // Application Support (FlutterCefPlugin.swift:702-728).
+ // current-user-SID protected DACL (the IPC pipe's hardening pattern applied
+ // to the profile tree). Mirrors macOS resolveProfileDir's stable 0700 dir
+ // under Application Support.
//
- // AT-REST NOTE (SPIKES.md S2 / §7): unlike macOS — where an ad-hoc (unsigned)
+ // AT-REST NOTE: unlike macOS — where an ad-hoc (unsigned)
// build has no real Keychain, so OSCrypt falls back to a MOCK key and named
// profiles are downgraded to ephemeral — Windows OSCrypt uses DPAPI, which is
// ALWAYS available and signing-INDEPENDENT. So a named profile just persists
diff --git a/packages/flutter_cef_windows/windows/flutter_cef_plugin.h b/packages/flutter_cef_windows/windows/flutter_cef_plugin.h
index 7e55f83..d38013a 100644
--- a/packages/flutter_cef_windows/windows/flutter_cef_plugin.h
+++ b/packages/flutter_cef_windows/windows/flutter_cef_plugin.h
@@ -1,9 +1,8 @@
// flutter_cef Windows plugin — channel host.
//
// The verb/event contract is ../native/cef_host/PROTOCOL.md §3/§4
-// (transcribed from FlutterCefPlugin.swift). Architecture (P6 foundation +
-// the profile slice of P11 — mirrors CefProfileHost.swift + FlutterCefPlugin
-// .swift):
+// (transcribed from FlutterCefPlugin.swift). Architecture (mirrors
+// CefProfileHost.swift + FlutterCefPlugin.swift):
// - ONE cef_host.exe per PROFILE. A non-empty `profile` create arg -> a
// shared, persistent Host (keyed by profile name) reused by every session
// that names it; an absent/empty profile -> an ephemeral Host, shared by a
@@ -20,19 +19,20 @@
// parent Host by key.
// - Reader/watcher threads never touch the MethodChannel: they post
// HostEvents into a mutex+deque drained on the platform thread via a
-// message-only HWND (PostMessage wakeup) — the slice-approved marshal.
+// message-only HWND (PostMessage wakeup).
// - Event routing: every host->plugin frame carries a wire browser_id.
// browser_id 0 is process-level (kOpReady, kOpLog); >=1 routes to the
-// Session bound to that id on that Host. The #1 generation guard now keys
-// on the HOST's generation, so a dead host's straggler frames (posted
+// Session bound to that id on that Host. The generation guard keys on
+// the HOST's generation, so a dead host's straggler frames (posted
// during the reaper grace after a same-profile respawn) can't reach a
// session on the fresh host.
// - Handshake: nothing is sent until kOpReady; protocolVersion must equal
// kCefHostProtocolVersion (else processGone "protocolMismatch(host=vN)" for
// every session). Verbs
// issued before ready are queued on the Host and flushed on ready.
-// - Present size-gate (LAW 4): a present is promoted only when its
-// {srcW,srcH} matches round(logical*dpr) ±1 px for the CURRENT size.
+// - Present size-gate: CEF still delivers late frames at the OLD size after
+// a resize, so a present is promoted only when its {srcW,srcH} matches
+// round(logical*dpr) ±1 px for the CURRENT size.
// - Teardown is two-tier: dispose ONE browser = kOpDisposeBrowser, host
// survives if other sessions remain; last session gone / host death = tear
// down the whole Host (reader/watcher/Job/pipe) via a bounded reaper.
@@ -74,7 +74,7 @@ namespace flutter_cef {
class CdpRelay; // windows/cdp_relay.h — winsock, pulled in only by the .cpp
-// Agent-control (P9) CDP-over-pipe transport for one host. Held via a
+// Agent-control CDP-over-pipe transport for one host. Held via a
// shared_ptr so the always-on CDP reader thread (which delivers pipe messages
// to the current relay) can safely outlive a Host erase during teardown — the
// reader captures its own shared_ptr, so the transport (and its handles) stay
@@ -117,8 +117,8 @@ class FlutterCefPlugin : public flutter::Plugin {
struct Host {
// hosts_ map key: profile name, "~group~"+hostGroup or "~ephemeral~"+sessionId
std::string key;
- // Monotonic per-spawn identity (C1 host-object-identity analogue of macOS
- // failHost, FlutterCefPlugin.swift:484-511). The reader/exit-watcher
+ // Monotonic per-spawn identity (the analogue of the host-object identity
+ // check in macOS failHost). The reader/exit-watcher
// lambdas capture THIS value; a stale OLD-host event posted during the
// reaper grace after a dispose+respawn of the SAME profile key carries a
// generation that no longer matches the live host and is dropped.
@@ -162,7 +162,7 @@ class FlutterCefPlugin : public flutter::Plugin {
int width = 800;
int height = 600;
double dpr = 1.0;
- // The size-gate expectation: round(logical * dpr) (LAW 4).
+ // The size-gate expectation: round(logical * dpr), in physical px.
uint32_t expected_pw = 0;
uint32_t expected_ph = 0;
uint64_t current_handle = 0; // last promoted bridge handle
@@ -221,7 +221,7 @@ class FlutterCefPlugin : public flutter::Plugin {
Kind kind = Kind::kFrame;
std::string host_key;
// The generation of the Host that owned the poster. Dropped on drain if it
- // no longer matches the live host's generation (C1).
+ // no longer matches the live host's generation.
uint64_t generation = 0;
uint32_t browser_id = 0;
uint8_t opcode = 0;
@@ -249,8 +249,8 @@ class FlutterCefPlugin : public flutter::Plugin {
Host* HostForSession(const Session* session);
// Resolve the live Host for `key`, or spawn a fresh one. nullptr on spawn
// failure. An EXISTING host is reused verbatim (its --allowed-schemes etc.
- // are process args fixed at its spawn — the reuse arg is ignored, macOS
- // parity, FlutterCefPlugin.swift:456-471).
+ // are process args fixed at its spawn — the reuse arg is ignored, as in
+ // macOS resolveOrSpawnHost).
Host* ResolveOrSpawnHost(const std::string& key,
const std::wstring& profile_dir, bool ephemeral,
const std::wstring& host_exe,
@@ -278,7 +278,7 @@ class FlutterCefPlugin : public flutter::Plugin {
// the last.
void DetachFromHost(const std::string& host_key, uint32_t browser_id);
- // Agent control (P9). enableAgentControl starts (idempotently) the token-gated
+ // Agent control. enableAgentControl starts (idempotently) the token-gated
// loopback CDP relay for the session's host and replies
// `{wsUrl, token, port}` (the macOS return shape exactly); it errors if the
// host was not created with agentControl:true. disableAgentControl tears the
@@ -324,7 +324,7 @@ class FlutterCefPlugin : public flutter::Plugin {
void HandleSessionFrame(Session* session, uint8_t opcode,
const std::vector& payload);
- // C1 first-present watchdog (WM_TIMER on message_window_).
+ // First-present watchdog (WM_TIMER on message_window_).
void ArmWatchdog(Session* session);
void CancelWatchdog(Session* session);
void OnWatchdogTimer(UINT_PTR timer_id);
@@ -343,9 +343,9 @@ class FlutterCefPlugin : public flutter::Plugin {
static std::wstring ResolveCefHostPath();
static std::wstring MakeEphemeralProfileDir();
// Persistent + shared profile dir: %LOCALAPPDATA%\flutter_cef\profiles\
- // , created with a current-user-SID protected DACL (the #3
- // pipe-hardening pattern). Empty string on an unusable name. See the .cpp
- // for the DPAPI-at-rest note (SPIKES.md S2 / §7).
+ // , created with a current-user-SID protected DACL (the
+ // IPC pipe's hardening pattern). Empty string on an unusable name. See
+ // the .cpp for the DPAPI-at-rest note.
static std::wstring MakePersistentProfileDir(const std::string& profile);
// Recursively delete a directory tree (best-effort). Safe on a missing path.
static void DeleteDirRecursive(const std::wstring& dir);
diff --git a/packages/flutter_cef_windows/windows/host_process.cpp b/packages/flutter_cef_windows/windows/host_process.cpp
index a95480a..66c6e51 100644
--- a/packages/flutter_cef_windows/windows/host_process.cpp
+++ b/packages/flutter_cef_windows/windows/host_process.cpp
@@ -54,7 +54,7 @@ bool HostProcess::Spawn(const std::wstring& cef_host_exe,
cmd += L" --allowed-schemes=" + w;
}
- // ---- Agent control (P9): CDP-over-pipe plumbing (the S3 recipe) ----
+ // ---- Agent control: CDP-over-pipe plumbing ----
// Two anonymous pipes; ONLY the child-side ends are inheritable and land in
// the STARTUPINFOEX handle list, so this composes cleanly with the existing
// spawn (which inherits nothing). cmd_pipe: parent writes CDP -> child reads.
@@ -78,8 +78,8 @@ bool HostProcess::Spawn(const std::wstring& cef_host_exe,
// Child argv: --remote-debugging-io-pipes wants , where read is
// the handle the browser READS commands from (cmd_read) and write is the one
// it WRITES responses to (out_write). Decimal HANDLE values, cast to
- // unsigned 32-bit (S3: `(unsigned)(uintptr_t)` — handle values fit in 32
- // bits for Chromium's int-parsing of the switch).
+ // unsigned 32-bit (`(unsigned)(uintptr_t)`: handle values fit in 32 bits
+ // for Chromium's int-parsing of the switch).
cmd += L" --cdp-io-pipes=" +
std::to_wstring(static_cast(
reinterpret_cast(cmd_read))) +
diff --git a/packages/flutter_cef_windows/windows/host_process.h b/packages/flutter_cef_windows/windows/host_process.h
index 97f20a3..0d5030d 100644
--- a/packages/flutter_cef_windows/windows/host_process.h
+++ b/packages/flutter_cef_windows/windows/host_process.h
@@ -5,7 +5,7 @@
// cef_host.exe --ipc= --profile-dir= [--ephemeral]
// [--allowed-schemes=] [--cdp-io-pipes=,]
// (named pipe: the child connects by NAME with CreateFileW, so no handle
-// inheritance is needed — cf. S3, which inherited anonymous handles).
+// inheritance is needed, unlike the agent-control CDP pipes below).
//
// Kill guarantees:
// - Job Object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, assigned before the
@@ -44,8 +44,8 @@ class HostProcess {
// CefProfileHost.spawn). A list that isn't all valid schemes is refused.
// Returns false on spawn failure.
//
- // AGENT CONTROL (P9): when `agent_control` is true, the spawn additionally
- // sets up the CDP-over-pipe transport (the S3 recipe, mirroring macOS
+ // AGENT CONTROL: when `agent_control` is true, the spawn additionally
+ // sets up the CDP-over-pipe transport (mirroring macOS
// launchViaPosixSpawn's fds 3/4): it CreatePipe()s two anonymous pipes, marks
// ONLY the child-side ends inheritable, spawns cef_host with a
// STARTUPINFOEX PROC_THREAD_ATTRIBUTE_HANDLE_LIST containing exactly those two
@@ -58,7 +58,7 @@ class HostProcess {
// `out_cdp_read` (we read CDP responses/events here; child writes) and
// `out_cdp_write` (we write CDP commands here; child reads). The caller owns +
// closes them. When `agent_control` is false the spawn is byte-identical to
- // the pre-P9 path (no handle inheritance, no extra pipes).
+ // a plain spawn (no handle inheritance, no extra pipes).
bool Spawn(const std::wstring& cef_host_exe, const std::wstring& pipe_name,
const std::wstring& profile_dir, bool ephemeral,
const std::string& allowed_schemes = std::string(),
@@ -92,7 +92,7 @@ class HostProcess {
private:
HANDLE process_ = nullptr; // held hProcess (no pid dance — a HANDLE is
- // not a recyclable global name, PLAN §4.2)
+ // not a recyclable global name)
HANDLE job_ = nullptr; // kill-on-close Job Object
};
diff --git a/packages/flutter_cef_windows/windows/ipc_pipe.cpp b/packages/flutter_cef_windows/windows/ipc_pipe.cpp
index 01f9092..663f198 100644
--- a/packages/flutter_cef_windows/windows/ipc_pipe.cpp
+++ b/packages/flutter_cef_windows/windows/ipc_pipe.cpp
@@ -11,7 +11,7 @@
#include "cef_host_protocol.h"
#include "current_user_sd.h"
-// PLAN §4.2/§7.6 pipe hardening needs a CSPRNG (BCryptGenRandom) and the token/
+// The pipe hardening needs a CSPRNG (BCryptGenRandom) and the token/
// SID/SDDL APIs. Neither bcrypt.lib nor advapi32.lib is on this plugin's CMake
// link line and that file is owned elsewhere — pull them in from the TU so the
// build stays self-contained.
@@ -46,7 +46,7 @@ IpcPipe::~IpcPipe() {
// static
std::wstring IpcPipe::NextPipeName() {
- // 128-bit CSPRNG name (PLAN §4.2/§7.6): a predictable pid_counter name lets a
+ // 128-bit CSPRNG name: a predictable pid_counter name lets a
// same-user process pre-create (squat) the pipe before cef_host connects. An
// unguessable name closes the race window entirely.
uint8_t rnd[16] = {};
@@ -71,7 +71,7 @@ std::wstring IpcPipe::NextPipeName() {
bool IpcPipe::Create(const std::wstring& pipe_name) {
pipe_name_ = pipe_name;
- // Explicit protected DACL granting ONLY the current user (PLAN §7.6): the
+ // Explicit protected DACL granting ONLY the current user: the
// default named-pipe DACL is broader than we want, and an anonymous descriptor
// gives no guarantee. Refuse to create an unsecured pipe.
PSECURITY_DESCRIPTOR sd = nullptr;
@@ -169,8 +169,8 @@ void IpcPipe::ReaderMain(FrameHandler handler,
if (!ReadFull(hdr, sizeof(hdr))) break;
const uint32_t body_len = ReadU32BE(hdr);
if (body_len < kMinBodyLen || body_len > kMaxBodyLen) {
- // Desynced stream: unrecoverable, tear the whole transport down
- // (main.mm:2343-2351 rule).
+ // Desynced stream: unrecoverable, tear the whole transport down (as the
+ // macOS host's IpcReadLoop in ipc_reader.mm does).
PipeLog("bodyLen out of range — stream desynced, closing");
break;
}
diff --git a/packages/flutter_cef_windows/windows/ipc_pipe.h b/packages/flutter_cef_windows/windows/ipc_pipe.h
index 1f60fe3..3acaad8 100644
--- a/packages/flutter_cef_windows/windows/ipc_pipe.h
+++ b/packages/flutter_cef_windows/windows/ipc_pipe.h
@@ -4,8 +4,8 @@
// constants header ../native/cef_host/cef_host_protocol.h.
//
// - Name: \\.\pipe\flutter_cef_<128-bit CSPRNG hex> (NextPipeName()). The
-// unguessable name + FILE_FLAG_FIRST_PIPE_INSTANCE close the squat race
-// (PLAN §4.2/§7.6): a predictable pid_counter name let a same-user process
+// unguessable name + FILE_FLAG_FIRST_PIPE_INSTANCE close the squat race:
+// a predictable pid_counter name let a same-user process
// pre-create the pipe before cef_host connected.
// - Server: CreateNamedPipeW(PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
// FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
@@ -25,13 +25,14 @@
// - Framing: [u32 bodyLen BE][u32 browserId BE][u8 op][payload], bodyLen =
// 4+1+payloadLen, guard 5..64 MiB (kMinBodyLen/kMaxBodyLen). Outbound
// frames are assembled contiguously and written whole under a write mutex
-// (as main.mm's SendFrame does), with a bounded wait (kWriteTimeoutMs).
+// (as SendFrame in the macOS host's ipc.mm does), with a bounded wait
+// (kWriteTimeoutMs).
// - The FrameHandler/DisconnectHandler run on the READER thread; the caller
// must marshal to the platform thread before touching the MethodChannel.
// - Teardown (Close): signal the stop event + CancelIoEx, bounded join. If
// the reader will not stop, Close returns false and the caller must LEAK
// this object (unique_ptr::release) — never free state a blocked reader
-// may still touch (CefProfileHost.swift:999-1002 rule).
+// may still touch (the rule CefProfileHost.shutdown follows on macOS).
//
// NOTE: deliberately flutter-free (windows.h + protocol header only) so it
// can be exercised by a standalone harness without an engine.
diff --git a/packages/flutter_cef_windows/windows/texture_bridge.cpp b/packages/flutter_cef_windows/windows/texture_bridge.cpp
index 097db92..5e99d7f 100644
--- a/packages/flutter_cef_windows/windows/texture_bridge.cpp
+++ b/packages/flutter_cef_windows/windows/texture_bridge.cpp
@@ -36,8 +36,8 @@ struct TextureBridge::Entry {
uint64_t handle = 0; // host-minted legacy shared handle — the identity
uint32_t width = 0;
uint32_t height = 0;
- // LAW 6 / S1 belt-1: opened D3D11 reference on the CURRENT bridge handle,
- // held for as long as it is fed to Flutter.
+ // Our own opened D3D11 reference on the CURRENT bridge handle, held for as
+ // long as it is fed to Flutter, so the texture outlives the host's release.
Microsoft::WRL::ComPtr keepalive;
std::unique_ptr variant;
};
@@ -51,7 +51,7 @@ bool TextureBridge::EnsureDevice() {
if (device_) return true;
// Any D3D11 device can open + hold a legacy MISC_SHARED handle; the engine
// opens the handle on ITS device via the descriptor. Ours only pins the
- // resource alive (LAW 6). Default adapter matches the spike-proven setup.
+ // resource alive. The default adapter is the setup the port was tested on.
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
HRESULT hr = D3D11CreateDevice(
nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, nullptr, 0,
@@ -91,7 +91,7 @@ int64_t TextureBridge::RegisterSessionTexture() {
raw->desc.height = raw->height;
raw->desc.visible_width = raw->width;
raw->desc.visible_height = raw->height;
- raw->desc.format = kFlutterDesktopPixelFormatNone; // LAW 5
+ raw->desc.format = kFlutterDesktopPixelFormatNone;
raw->desc.release_context = nullptr;
raw->desc.release_callback = [](void*) {};
return &raw->desc;
@@ -125,8 +125,8 @@ bool TextureBridge::Present(int64_t texture_id, uint64_t bridge_handle,
changed = (entry->handle != bridge_handle);
}
- // `previous` keeps the old opened ref alive until AFTER the swap (LAW 6:
- // release the previous only once the new one is being served).
+ // `previous` keeps the old opened ref alive until AFTER the swap: release
+ // the previous only once the new one is being served.
Microsoft::WRL::ComPtr previous;
if (changed) {
if (!EnsureDevice()) return false;
@@ -135,7 +135,7 @@ bool TextureBridge::Present(int64_t texture_id, uint64_t bridge_handle,
reinterpret_cast(static_cast(bridge_handle)),
IID_PPV_ARGS(opened.GetAddressOf()));
if (FAILED(hr)) {
- // #9: on device loss (removed/reset) the cached device is dead and every
+ // On device loss (removed/reset) the cached device is dead and every
// OpenSharedResource on it keeps failing — drop it (+ this slot's opened
// keepalive) so the next present re-creates the device via EnsureDevice
// and reopens the host-re-minted handle fresh. A plain bad-handle miss
@@ -193,7 +193,7 @@ void TextureBridge::Unregister(int64_t texture_id) {
entry = std::move(it->second);
entries_.erase(it);
}
- // Async unregister (PLAN §2 #7): the engine may still sample the texture /
+ // Async unregister: the engine may still sample the texture /
// call the descriptor callback until the completion callback fires. The
// captured shared_ptr keeps the entry (variant + descriptor + keep-alive
// ComPtr) alive exactly until the engine runs or destroys the callback —
diff --git a/packages/flutter_cef_windows/windows/texture_bridge.h b/packages/flutter_cef_windows/windows/texture_bridge.h
index 14c3f0b..b05e101 100644
--- a/packages/flutter_cef_windows/windows/texture_bridge.h
+++ b/packages/flutter_cef_windows/windows/texture_bridge.h
@@ -1,24 +1,25 @@
// TextureBridge — owns the Flutter GPU texture a session presents into.
//
-// The real bridge, per PROTOCOL.md §5 and the LAWS:
+// The real bridge, per PROTOCOL.md §5:
// - Each session gets a flutter::GpuSurfaceTexture
// (kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle) whose descriptor callback
-// serves the CURRENT host-minted bridge handle under a mutex
-// (validated by the P0 S1 spike — see specs/windows-port/SPIKES.md).
+// serves the CURRENT host-minted bridge handle under a mutex.
// - Descriptor: struct_size SET, width/height AND visible_width/height,
-// format = kFlutterDesktopPixelFormatNone (LAW 5). Returns nullptr before
+// format = kFlutterDesktopPixelFormatNone. Returns nullptr before
// the first present — the engine skips the frame (PopulateTexture checks
// descriptor/handle for null and bails).
// - Present() holds an opened ID3D11Texture2D ComPtr on the CURRENT bridge
// handle for as long as it is fed to Flutter, releasing the previous one
-// only AFTER the swap (LAW 6, S1 belt-1).
+// only AFTER the swap, so the texture stays alive even once the host
+// releases its bridge.
// - Identity is the host-minted bridge handle value we are handed — never
-// CEF's per-callback shared_texture_handle (LAW 3). The size-gate (LAW 4)
-// is the CALLER's job (the plugin compares srcW/srcH against the expected
-// round(logical*dpr) before calling Present).
+// CEF's per-callback shared_texture_handle (those values alias across sizes
+// and browsers). The size-gate is the CALLER's job (the plugin compares
+// srcW/srcH against the expected round(logical*dpr) before calling
+// Present).
// - UnregisterTexture is ASYNC on Windows: the entry (variant + descriptor +
// keep-alive ComPtr) is captured by the completion callback and freed when
-// the engine releases it (PLAN §2 #7) — no graveyard, no leak-to-teardown.
+// the engine releases it — no graveyard, no leak-to-teardown.
#ifndef FLUTTER_PLUGIN_FLUTTER_CEF_TEXTURE_BRIDGE_H_
#define FLUTTER_PLUGIN_FLUTTER_CEF_TEXTURE_BRIDGE_H_
@@ -51,7 +52,8 @@ class TextureBridge {
// the texture's backing surface and marks a frame available.
// width/height are the frame's physical px (already size-gated by the
// caller). On a handle change, opens the handle on our D3D11 device and
- // holds the ComPtr (LAW 6), releasing the previous ref AFTER the swap.
+ // holds the ComPtr to keep the texture alive, releasing the previous ref
+ // AFTER the swap.
// Returns false if the entry is unknown or the handle could not be opened
// (previous texture keeps serving). *handle_changed reports whether the
// backing surface identity changed (drives the onSurface event).