mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-30 15:41:48 +00:00
Add a dumb renderer to get coordinates
FlatRenderer positions items on a single row, giving each a height and width of 1. Added getBoundingClientRect to the DOMelement which, when requested for the first time, will place the item in with the renderer. The goal here is to give elements a fixed position and to make it easy to map x,y coordinates onto an element. This should work, at least with puppeteer, since it first requests the boundingClientRect before issuing a click.
This commit is contained in:
@@ -37,6 +37,7 @@ pub const CDP = CDPT(struct {
|
||||
|
||||
const SessionIdGen = Incrementing(u32, "SID");
|
||||
const TargetIdGen = Incrementing(u32, "TID");
|
||||
const LoaderIdGen = Incrementing(u32, "LID");
|
||||
const BrowserContextIdGen = Incrementing(u32, "BID");
|
||||
|
||||
// Generic so that we can inject mocks into it.
|
||||
@@ -54,6 +55,7 @@ pub fn CDPT(comptime TypeProvider: type) type {
|
||||
target_auto_attach: bool = false,
|
||||
|
||||
target_id_gen: TargetIdGen = .{},
|
||||
loader_id_gen: LoaderIdGen = .{},
|
||||
session_id_gen: SessionIdGen = .{},
|
||||
browser_context_id_gen: BrowserContextIdGen = .{},
|
||||
|
||||
@@ -183,6 +185,7 @@ pub fn CDPT(comptime TypeProvider: type) type {
|
||||
},
|
||||
5 => switch (@as(u40, @bitCast(domain[0..5].*))) {
|
||||
asUint("Fetch") => return @import("domains/fetch.zig").processMessage(command),
|
||||
asUint("Input") => return @import("domains/input.zig").processMessage(command),
|
||||
else => {},
|
||||
},
|
||||
6 => switch (@as(u48, @bitCast(domain[0..6].*))) {
|
||||
|
||||
80
src/cdp/domains/input.zig
Normal file
80
src/cdp/domains/input.zig
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
pub fn processMessage(cmd: anytype) !void {
|
||||
const action = std.meta.stringToEnum(enum {
|
||||
dispatchMouseEvent,
|
||||
}, cmd.input.action) orelse return error.UnknownMethod;
|
||||
|
||||
switch (action) {
|
||||
.dispatchMouseEvent => return dispatchMouseEvent(cmd),
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
})) orelse return error.InvalidParams;
|
||||
|
||||
try cmd.sendResult(null, .{});
|
||||
|
||||
if (std.ascii.eqlIgnoreCase(params.type, "mousePressed") == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
switch (click_result) {
|
||||
.navigate => |uri| try clickNavigate(cmd, uri),
|
||||
}
|
||||
// result already sent
|
||||
}
|
||||
|
||||
fn clickNavigate(cmd: anytype, uri: std.Uri) !void {
|
||||
const bc = cmd.browser_context.?;
|
||||
|
||||
var url_buf: std.ArrayListUnmanaged(u8) = .{};
|
||||
try uri.writeToStream(.{
|
||||
.scheme = true,
|
||||
.authentication = true,
|
||||
.authority = true,
|
||||
.port = true,
|
||||
.path = true,
|
||||
.query = true,
|
||||
}, url_buf.writer(cmd.arena));
|
||||
const url = url_buf.items;
|
||||
|
||||
try cmd.sendEvent("Page.frameRequestedNavigation", .{
|
||||
.url = url,
|
||||
.frameId = bc.target_id.?,
|
||||
.reason = "anchorClick",
|
||||
.disposition = "currentTab",
|
||||
}, .{ .session_id = bc.session_id.? });
|
||||
|
||||
bc.session.removePage();
|
||||
_ = try bc.session.createPage(null);
|
||||
|
||||
try @import("page.zig").navigateToUrl(cmd, url, false);
|
||||
}
|
||||
@@ -129,6 +129,18 @@ fn createIsolatedWorld(cmd: anytype) !void {
|
||||
}
|
||||
|
||||
fn navigate(cmd: anytype) !void {
|
||||
const params = (try cmd.params(struct {
|
||||
url: []const u8,
|
||||
// referrer: ?[]const u8 = null,
|
||||
// transitionType: ?[]const u8 = null, // TODO: enum
|
||||
// frameId: ?[]const u8 = null,
|
||||
// referrerPolicy: ?[]const u8 = null, // TODO: enum
|
||||
})) orelse return error.InvalidParams;
|
||||
|
||||
return navigateToUrl(cmd, params.url, true);
|
||||
}
|
||||
|
||||
pub fn navigateToUrl(cmd: anytype, url: []const u8, send_result: bool) !void {
|
||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||
|
||||
// didn't create?
|
||||
@@ -140,20 +152,10 @@ fn navigate(cmd: anytype) !void {
|
||||
// if we have a target_id we have to have a page;
|
||||
std.debug.assert(bc.session.page != null);
|
||||
|
||||
const params = (try cmd.params(struct {
|
||||
url: []const u8,
|
||||
referrer: ?[]const u8 = null,
|
||||
transitionType: ?[]const u8 = null, // TODO: enum
|
||||
frameId: ?[]const u8 = null,
|
||||
referrerPolicy: ?[]const u8 = null, // TODO: enum
|
||||
})) orelse return error.InvalidParams;
|
||||
|
||||
// change state
|
||||
bc.reset();
|
||||
bc.url = params.url;
|
||||
|
||||
// TODO: hard coded ID
|
||||
bc.loader_id = "AF8667A203C5392DBE9AC290044AA4C2";
|
||||
bc.url = url;
|
||||
bc.loader_id = cmd.cdp.loader_id_gen.next();
|
||||
|
||||
const LifecycleEvent = struct {
|
||||
frameId: []const u8,
|
||||
@@ -180,10 +182,12 @@ fn navigate(cmd: anytype) !void {
|
||||
}
|
||||
|
||||
// output
|
||||
try cmd.sendResult(.{
|
||||
.frameId = target_id,
|
||||
.loaderId = bc.loader_id,
|
||||
}, .{});
|
||||
if (send_result) {
|
||||
try cmd.sendResult(.{
|
||||
.frameId = target_id,
|
||||
.loaderId = bc.loader_id,
|
||||
}, .{});
|
||||
}
|
||||
|
||||
// TODO: at this point do we need async the following actions to be async?
|
||||
|
||||
@@ -199,7 +203,7 @@ fn navigate(cmd: anytype) !void {
|
||||
);
|
||||
|
||||
var page = bc.session.currentPage().?;
|
||||
try page.navigate(params.url, aux_data);
|
||||
try page.navigate(url, aux_data);
|
||||
|
||||
// Events
|
||||
|
||||
@@ -218,7 +222,7 @@ fn navigate(cmd: anytype) !void {
|
||||
.type = "Navigation",
|
||||
.frame = Frame{
|
||||
.id = target_id,
|
||||
.url = bc.url,
|
||||
.url = url,
|
||||
.securityOrigin = bc.security_origin,
|
||||
.secureContextType = bc.secure_context_type,
|
||||
.loaderId = bc.loader_id,
|
||||
|
||||
@@ -106,11 +106,17 @@ const Page = struct {
|
||||
aux_data: []const u8 = "",
|
||||
doc: ?*parser.Document = null,
|
||||
|
||||
pub fn navigate(self: *Page, url: []const u8, aux_data: []const u8) !void {
|
||||
_ = self;
|
||||
pub fn navigate(_: *Page, url: []const u8, aux_data: []const u8) !void {
|
||||
_ = url;
|
||||
_ = aux_data;
|
||||
}
|
||||
|
||||
const ClickResult = @import("../browser/browser.zig").Page.ClickResult;
|
||||
pub fn click(_: *Page, _: Allocator, x: u32, y: u32) !?ClickResult {
|
||||
_ = x;
|
||||
_ = y;
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const Client = struct {
|
||||
|
||||
Reference in New Issue
Block a user