From b76875bf5d65ff9e3caca9d7bccbed295cb8e807 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Mon, 7 Apr 2025 11:50:45 +0800 Subject: [PATCH] use netsurf's mousevent --- src/browser/browser.zig | 38 ++++++++++++++++++------ src/cdp/domains/input.zig | 32 ++++++++++++++++---- src/netsurf/netsurf.zig | 62 +++++++++++++++++++++++++++++++++++---- vendor/zig-js-runtime | 2 +- 4 files changed, 113 insertions(+), 21 deletions(-) diff --git a/src/browser/browser.zig b/src/browser/browser.zig index 7bdeff66..14d85df1 100644 --- a/src/browser/browser.zig +++ b/src/browser/browser.zig @@ -431,14 +431,34 @@ pub const Page = struct { navigate: std.Uri, }; - pub fn click(self: *Page, allocator: Allocator, x: u32, y: u32) !?ClickResult { - const element = self.renderer.getElementAtPosition(x, y) orelse return null; + pub const MouseEvent = struct { + x: i32, + y: i32, + type: Type, - const event = try parser.eventCreate(); - defer parser.eventDestroy(event); + const Type = enum { + pressed, + released, + }; + }; - try parser.eventInit(event, "click", .{ .bubbles = true, .cancelable = true }); - if ((try parser.eventDefaultPrevented(event)) == true) { + pub fn mouseEvent(self: *Page, allocator: Allocator, me: MouseEvent) !?ClickResult { + if (me.type != .pressed) { + return null; + } + + const element = self.renderer.getElementAtPosition(me.x, me.y) orelse return null; + + const event = try parser.mouseEventCreate(); + defer parser.mouseEventDestroy(event); + + try parser.mouseEventInit(event, "click", .{ + .bubbles = true, + .cancelable = true, + .x = me.x, + .y = me.y, + }); + if ((try parser.mouseEventDefaultPrevented(event)) == true) { return null; } @@ -838,13 +858,13 @@ const FlatRenderer = struct { return 1; } - pub fn getElementAtPosition(self: *const FlatRenderer, x: u32, y: u32) ?*parser.Element { - if (y > 1) { + pub fn getElementAtPosition(self: *const FlatRenderer, x: i32, y: i32) ?*parser.Element { + if (y != 1 or x < 0) { return null; } const elements = self.elements.items; - return if (x < elements.len) @ptrFromInt(elements[x]) else null; + return if (x < elements.len) @ptrFromInt(elements[@intCast(x)]) else null; } }; diff --git a/src/cdp/domains/input.zig b/src/cdp/domains/input.zig index 44c39eb5..196ed192 100644 --- a/src/cdp/domains/input.zig +++ b/src/cdp/domains/input.zig @@ -17,6 +17,7 @@ // along with this program. If not, see . const std = @import("std"); +const Page = @import("../../browser/browser.zig").Page; pub fn processMessage(cmd: anytype) !void { const action = std.meta.stringToEnum(enum { @@ -31,20 +32,39 @@ pub fn processMessage(cmd: anytype) !void { // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchMouseEvent fn dispatchMouseEvent(cmd: anytype) !void { const params = (try cmd.params(struct { - type: []const u8, - x: u32, - y: u32, + x: i32, + y: i32, + type: Type, + + const Type = enum { + mousePressed, + mouseReleased, + mouseMoved, + mouseWheel, + }; })) orelse return error.InvalidParams; try cmd.sendResult(null, .{}); - if (std.ascii.eqlIgnoreCase(params.type, "mousePressed") == false) { - return; + // quickly ignore types we know we don't handle + switch (params.type) { + .mouseMoved, .mouseWheel => return, + else => {}, } const bc = cmd.browser_context orelse return; const page = bc.session.currentPage() orelse return; - const click_result = (try page.click(cmd.arena, params.x, params.y)) orelse return; + + const mouse_event = Page.MouseEvent{ + .x = params.x, + .y = params.y, + .type = switch (params.type) { + .mousePressed => .pressed, + .mouseReleased => .released, + else => unreachable, + }, + }; + const click_result = (try page.mouseEvent(cmd.arena, mouse_event)) orelse return; switch (click_result) { .navigate => |uri| try clickNavigate(cmd, uri), diff --git a/src/netsurf/netsurf.zig b/src/netsurf/netsurf.zig index 2471b2b2..109c547f 100644 --- a/src/netsurf/netsurf.zig +++ b/src/netsurf/netsurf.zig @@ -24,6 +24,7 @@ const c = @cImport({ @cInclude("dom/bindings/hubbub/parser.h"); @cInclude("events/event_target.h"); @cInclude("events/event.h"); + @cInclude("events/mouse_event.h"); }); const mimalloc = @import("mimalloc"); @@ -809,14 +810,10 @@ const DispatchOpts = struct { pub fn elementDispatchEvent(element: *Element, opts: DispatchOpts) !bool { const event = try eventCreate(); defer eventDestroy(event); - try eventInit(event, opts.type, .{ .bubbles = opts.bubbles, .cancelable = opts.cancelable }); - var res: bool = undefined; const et: *EventTarget = @ptrCast(element); - const err = eventTargetVtable(et).dispatch_event.?(et, event, &res); - try DOMErr(err); - return res; + return eventTargetDispatchEvent(et, event); } pub fn eventTargetTBaseFieldName(comptime T: type) ?[]const u8 { @@ -878,6 +875,61 @@ pub const EventTargetTBase = extern struct { } }; +// MouseEvent + +pub const MouseEvent = c.dom_mouse_event; + +pub fn mouseEventCreate() !*MouseEvent { + var evt: ?*MouseEvent = undefined; + const err = c._dom_mouse_event_create(&evt); + try DOMErr(err); + return evt.?; +} + +pub fn mouseEventDestroy(evt: *MouseEvent) void { + c._dom_mouse_event_destroy(evt); +} + +const MouseEventOpts = struct { + x: i32, + y: i32, + bubbles: bool = false, + cancelable: bool = false, + ctrl: bool = false, + alt: bool = false, + shift: bool = false, + meta: bool = false, + button: u16 = 0, + click_count: u16 = 1, +}; + +pub fn mouseEventInit(evt: *MouseEvent, typ: []const u8, opts: MouseEventOpts) !void { + const s = try strFromData(typ); + const err = c._dom_mouse_event_init( + evt, + s, + opts.bubbles, + opts.cancelable, + null, // dom_abstract_view* ? + opts.click_count, // details + opts.x, // screen_x + opts.y, // screen_y + opts.x, // client_x + opts.y, // client_y + opts.ctrl, + opts.alt, + opts.shift, + opts.meta, + opts.button, + null, // related target + ); + try DOMErr(err); +} + +pub fn mouseEventDefaultPrevented(evt: *MouseEvent) !bool { + return eventDefaultPrevented(@ptrCast(evt)); +} + // NodeType pub const NodeType = enum(u4) { diff --git a/vendor/zig-js-runtime b/vendor/zig-js-runtime index 304a12e3..6b48960a 160000 --- a/vendor/zig-js-runtime +++ b/vendor/zig-js-runtime @@ -1 +1 @@ -Subproject commit 304a12e3174f73fc92ff20b90baca4786f233942 +Subproject commit 6b48960a0664b74016c777da5e3f9129e5f041f7