Merge pull request #1299 from lightpanda-io/cdp-inserttext

backport cdp Input.insertText
This commit is contained in:
Karl Seguin
2025-12-29 19:11:46 +08:00
committed by GitHub
5 changed files with 67 additions and 1 deletions

View File

@@ -2500,6 +2500,13 @@ pub fn handleKeydown(self: *Page, target: *Node, event: *Event) !void {
// Handle printable characters // Handle printable characters
if (key.isPrintable()) { if (key.isPrintable()) {
// if the input is selected, replace the content.
if (input._selected) {
const new_value = try self.arena.dupe(u8, key.asString());
try input.setValue(new_value, self);
input._selected = false;
return;
}
const current_value = input.getValue(); const current_value = input.getValue();
const new_value = try std.mem.concat(self.arena, u8, &.{ current_value, key.asString() }); const new_value = try std.mem.concat(self.arena, u8, &.{ current_value, key.asString() });
try input.setValue(new_value, self); try input.setValue(new_value, self);
@@ -2561,6 +2568,37 @@ pub fn submitForm(self: *Page, submitter_: ?*Element, form_: ?*Element.Html.Form
return self.scheduleNavigation(action, opts, .form); return self.scheduleNavigation(action, opts, .form);
} }
// insertText is a shortcut to insert text into the active element.
pub fn insertText(self: *Page, v: []const u8) !void {
const html_element = self.document._active_element orelse return;
if (html_element.is(Element.Html.Input)) |input| {
const input_type = input._input_type;
if (input_type == .radio or input_type == .checkbox) {
return;
}
// If the input is selected, replace the existing value
if (input._selected) {
const new_value = try self.arena.dupe(u8, v);
try input.setValue(new_value, self);
input._selected = false;
return;
}
// Or append the value
const current_value = input.getValue();
const new_value = try std.mem.concat(self.arena, u8, &.{ current_value, v });
return input.setValue(new_value, self);
}
if (html_element.is(Element.Html.TextArea)) |textarea| {
const current_value = textarea.getValue();
const new_value = try std.mem.concat(self.arena, u8, &.{ current_value, v });
return textarea.setValue(new_value, self);
}
}
const RequestCookieOpts = struct { const RequestCookieOpts = struct {
is_http: bool = true, is_http: bool = true,
is_navigation: bool = false, is_navigation: bool = false,

View File

@@ -594,6 +594,11 @@ pub fn getAdoptedStyleSheets(self: *Document, page: *Page) !js.Object {
return obj; return obj;
} }
pub fn hasFocus(_: *Document) bool {
log.debug(.not_implemented, "Document.hasFocus", .{});
return true;
}
pub fn setAdoptedStyleSheets(self: *Document, sheets: js.Object) !void { pub fn setAdoptedStyleSheets(self: *Document, sheets: js.Object) !void {
self._adopted_style_sheets = try sheets.persist(); self._adopted_style_sheets = try sheets.persist();
} }
@@ -675,6 +680,7 @@ pub const JsApi = struct {
return page.window; return page.window;
} }
}.defaultView, null, .{ .cache = "defaultView" }); }.defaultView, null, .{ .cache = "defaultView" });
pub const hasFocus = bridge.function(Document.hasFocus, .{});
}; };
const testing = @import("../../testing.zig"); const testing = @import("../../testing.zig");

View File

@@ -75,6 +75,7 @@ _value: ?[]const u8 = null,
_checked: bool = false, _checked: bool = false,
_checked_dirty: bool = false, _checked_dirty: bool = false,
_input_type: Type = .text, _input_type: Type = .text,
_selected: bool = false,
pub fn asElement(self: *Input) *Element { pub fn asElement(self: *Input) *Element {
return self._proto._proto; return self._proto._proto;
@@ -246,6 +247,10 @@ pub fn setRequired(self: *Input, required: bool, page: *Page) !void {
} }
} }
pub fn select(self: *Input) void {
self._selected = true;
}
pub fn getForm(self: *Input, page: *Page) ?*Form { pub fn getForm(self: *Input, page: *Page) ?*Form {
const element = self.asElement(); const element = self.asElement();
@@ -337,6 +342,7 @@ pub const JsApi = struct {
pub const size = bridge.accessor(Input.getSize, Input.setSize, .{}); pub const size = bridge.accessor(Input.getSize, Input.setSize, .{});
pub const src = bridge.accessor(Input.getSrc, Input.setSrc, .{}); pub const src = bridge.accessor(Input.getSrc, Input.setSrc, .{});
pub const form = bridge.accessor(Input.getForm, null, .{}); pub const form = bridge.accessor(Input.getForm, null, .{});
pub const select = bridge.function(Input.select, .{});
}; };
pub const Build = struct { pub const Build = struct {

View File

@@ -22,11 +22,13 @@ pub fn processMessage(cmd: anytype) !void {
const action = std.meta.stringToEnum(enum { const action = std.meta.stringToEnum(enum {
dispatchKeyEvent, dispatchKeyEvent,
dispatchMouseEvent, dispatchMouseEvent,
insertText,
}, cmd.input.action) orelse return error.UnknownMethod; }, cmd.input.action) orelse return error.UnknownMethod;
switch (action) { switch (action) {
.dispatchKeyEvent => return dispatchKeyEvent(cmd), .dispatchKeyEvent => return dispatchKeyEvent(cmd),
.dispatchMouseEvent => return dispatchMouseEvent(cmd), .dispatchMouseEvent => return dispatchMouseEvent(cmd),
.insertText => return insertText(cmd),
} }
} }
@@ -101,6 +103,20 @@ fn dispatchMouseEvent(cmd: anytype) !void {
// result already sent // result already sent
} }
// https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-insertText
fn insertText(cmd: anytype) !void {
const params = (try cmd.params(struct {
text: []const u8, // The text to insert
})) orelse return error.InvalidParams;
const bc = cmd.browser_context orelse return;
const page = bc.session.currentPage() orelse return;
try page.insertText(params.text);
try cmd.sendResult(null, .{});
}
fn clickNavigate(cmd: anytype, uri: std.Uri) !void { fn clickNavigate(cmd: anytype, uri: std.Uri) !void {
const bc = cmd.browser_context.?; const bc = cmd.browser_context.?;

View File

@@ -159,7 +159,7 @@ fn logLogfmt(comptime scope: Scope, level: Level, comptime msg: []const u8, data
inline for (@typeInfo(@TypeOf(data)).@"struct".fields) |f| { inline for (@typeInfo(@TypeOf(data)).@"struct".fields) |f| {
const value = @field(data, f.name); const value = @field(data, f.name);
if (std.meta.hasMethod(@TypeOf(value), "logFmt")) { if (std.meta.hasMethod(@TypeOf(value), "logFmt")) {
try value.logFmt(f.name, LogFormatWriter{.writer = writer}); try value.logFmt(f.name, LogFormatWriter{ .writer = writer });
} else { } else {
const key = " " ++ f.name ++ "="; const key = " " ++ f.name ++ "=";
try writer.writeAll(key); try writer.writeAll(key);