ReviewOS

also looking at this

craft-native/craft

feat: add Linux and Windows named-window routing

#325
Merged glennmichael123 wants to merge codex/multi-window-linux-windows into main
29 files +2250 -200

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

docs/architecture/multi-window-ownership.mdmodified+2-1
Changes to docs/architecture/multi-window-ownership.md
@@ -24,7 +24,8 @@ app-local WebView2 loader; users still need the Microsoft WebView2 Runtime.
2424
2525Linux and Windows keep their live native window/webview pairs in
2626`desktop_window_registry.zig`. Page messages are authenticated by the sending
27WebKitGTK/WebView2 view; names select a target only after that sender is known.
27WebKitGTK view or the per-WebView2 event subscription; names select a target
28only after that native source is known.
2829Replies go back to the requesting view, and lifecycle events go to the changed
2930window plus its named handle's creator. Linux must register its `GtkApplication`
3031before constructing the first `GtkApplicationWindow`, because the CLI creates
packages/zig/src/desktop_window_registry.zigmodified+10-0
Changes to packages/zig/src/desktop_window_registry.zig
@@ -282,3 +282,13 @@ test "a window keeps its own platform control object" {
282282 _ = registry.forgetWindow(0x1000);
283283 try std.testing.expectEqual(@as(usize, 0x2002), registry.byWindow(0x2000).?.context);
284284}
285
286test "a stale event subscription id cannot select a reopened window" {
287 var registry: Registry = .{};
288 const old_id = registry.remember(0x1000, 0x1001).?;
289 _ = registry.forgetWindow(0x1000);
290 const reopened_id = registry.remember(0x1000, 0x2001).?;
291 try std.testing.expect(old_id != reopened_id);
292 try std.testing.expect(registry.byId(old_id) == null);
293 try std.testing.expectEqual(@as(usize, 0x2001), registry.byId(reopened_id).?.webview);
294}
packages/zig/src/windows.zigmodified+8-4
Changes to packages/zig/src/windows.zig
@@ -641,6 +641,7 @@ pub const ICoreWebView2PermissionRequestedEventHandler = PermissionRequestedHand
641641const WebMessageReceivedHandler = extern struct {
642642 lpVtbl: *const WebMessageReceivedHandlerVtbl,
643643 ref_count: c_ulong,
644 window_id: u32,
644645
645646 const WebMessageReceivedHandlerVtbl = extern struct {
646647 QueryInterface: *const fn (*WebMessageReceivedHandler, *const GUID, *?*anyopaque) callconv(.c) HRESULT,
@@ -674,9 +675,12 @@ const WebMessageReceivedHandler = extern struct {
674675 return remaining;
675676 }
676677
677 fn invoke(_: *WebMessageReceivedHandler, sender: *ICoreWebView2, args: *ICoreWebView2WebMessageReceivedEventArgs) callconv(.c) HRESULT {
678 // Authenticate with the COM sender, never an id supplied by the page.
679 const entry = desktop_windows.byWebview(@intFromPtr(sender)) orelse return S_OK;
678 fn invoke(self: *WebMessageReceivedHandler, _: *ICoreWebView2, args: *ICoreWebView2WebMessageReceivedEventArgs) callconv(.c) HRESULT {
679 // The handler is registered on exactly one native WebView2 instance.
680 // Bind its live registry id rather than comparing raw COM interface
681 // pointers, which need not have the same address for one object.
682 const entry = desktop_windows.byId(self.window_id) orelse return S_OK;
683 std.debug.print("[WebView2] Page message received for window {d}\n", .{self.window_id});
680684 var message_wide: ?LPWSTR = null;
681685 if (!succeeded(args.lpVtbl.get_WebMessageAsJson(args, &message_wide))) return S_OK;
682686 const wide = message_wide orelse return S_OK;
@@ -1242,7 +1246,7 @@ pub const Window = struct {
12421246 std.debug.print("[WebView2] Native window registered\n", .{});
12431247
12441248 const message_handler = try std.heap.c_allocator.create(WebMessageReceivedHandler);
1245 message_handler.* = .{ .lpVtbl = &WebMessageReceivedHandler.vtbl_instance, .ref_count = 1 };
1249 message_handler.* = .{ .lpVtbl = &WebMessageReceivedHandler.vtbl_instance, .ref_count = 1, .window_id = window_id };
12461250 var message_token: EventRegistrationToken = .{ .value = 0 };
12471251 std.debug.print("[WebView2] Registering page message handler\n", .{});
12481252 const message_hr = webview.lpVtbl.add_WebMessageReceived(webview, @ptrCast(message_handler), &message_token);