also looking at this
feat: add Linux and Windows named-window routing
#325
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.
| @@ -24,7 +24,8 @@ app-local WebView2 loader; users still need the Microsoft WebView2 Runtime. | ||
| 24 | 24 | |
| 25 | 25 | Linux and Windows keep their live native window/webview pairs in |
| 26 | 26 | `desktop_window_registry.zig`. Page messages are authenticated by the sending |
| 27 | WebKitGTK/WebView2 view; names select a target only after that sender is known. | |
| 27 | WebKitGTK view or the per-WebView2 event subscription; names select a target | |
| 28 | only after that native source is known. | |
| 28 | 29 | Replies go back to the requesting view, and lifecycle events go to the changed |
| 29 | 30 | window plus its named handle's creator. Linux must register its `GtkApplication` |
| 30 | 31 | before constructing the first `GtkApplicationWindow`, because the CLI creates |
| @@ -282,3 +282,13 @@ test "a window keeps its own platform control object" { | ||
| 282 | 282 | _ = registry.forgetWindow(0x1000); |
| 283 | 283 | try std.testing.expectEqual(@as(usize, 0x2002), registry.byWindow(0x2000).?.context); |
| 284 | 284 | } |
| 285 | ||
| 286 | test "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 | } | |
| @@ -641,6 +641,7 @@ pub const ICoreWebView2PermissionRequestedEventHandler = PermissionRequestedHand | ||
| 641 | 641 | const WebMessageReceivedHandler = extern struct { |
| 642 | 642 | lpVtbl: *const WebMessageReceivedHandlerVtbl, |
| 643 | 643 | ref_count: c_ulong, |
| 644 | window_id: u32, | |
| 644 | 645 | |
| 645 | 646 | const WebMessageReceivedHandlerVtbl = extern struct { |
| 646 | 647 | QueryInterface: *const fn (*WebMessageReceivedHandler, *const GUID, *?*anyopaque) callconv(.c) HRESULT, |
| @@ -674,9 +675,12 @@ const WebMessageReceivedHandler = extern struct { | ||
| 674 | 675 | return remaining; |
| 675 | 676 | } |
| 676 | 677 | |
| 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}); | |
| 680 | 684 | var message_wide: ?LPWSTR = null; |
| 681 | 685 | if (!succeeded(args.lpVtbl.get_WebMessageAsJson(args, &message_wide))) return S_OK; |
| 682 | 686 | const wide = message_wide orelse return S_OK; |
| @@ -1242,7 +1246,7 @@ pub const Window = struct { | ||
| 1242 | 1246 | std.debug.print("[WebView2] Native window registered\n", .{}); |
| 1243 | 1247 | |
| 1244 | 1248 | 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 }; | |
| 1246 | 1250 | var message_token: EventRegistrationToken = .{ .value = 0 }; |
| 1247 | 1251 | std.debug.print("[WebView2] Registering page message handler\n", .{}); |
| 1248 | 1252 | const message_hr = webview.lpVtbl.add_WebMessageReceived(webview, @ptrCast(message_handler), &message_token); |