mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
@@ -1,8 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const server = @import("../server.zig");
|
const server = @import("../server.zig");
|
||||||
const Ctx = server.CmdContext;
|
const Ctx = server.Cmd;
|
||||||
const SendFn = server.SendFn;
|
|
||||||
const result = @import("cdp.zig").result;
|
const result = @import("cdp.zig").result;
|
||||||
const getParams = @import("cdp.zig").getParams;
|
const getParams = @import("cdp.zig").getParams;
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ pub fn browser(
|
|||||||
ctx: *Ctx,
|
ctx: *Ctx,
|
||||||
) ![]const u8 {
|
) ![]const u8 {
|
||||||
const method = std.meta.stringToEnum(BrowserMethods, action) orelse
|
const method = std.meta.stringToEnum(BrowserMethods, action) orelse
|
||||||
return error.UnknownBrowserMethod;
|
return error.UnknownMethod;
|
||||||
return switch (method) {
|
return switch (method) {
|
||||||
.getVersion => browserGetVersion(alloc, id, scanner, ctx),
|
.getVersion => browserGetVersion(alloc, id, scanner, ctx),
|
||||||
.setDownloadBehavior => browserSetDownloadBehavior(alloc, id, scanner, ctx),
|
.setDownloadBehavior => browserSetDownloadBehavior(alloc, id, scanner, ctx),
|
||||||
|
|||||||
@@ -1,11 +1,26 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const server = @import("../server.zig");
|
const server = @import("../server.zig");
|
||||||
const Ctx = server.CmdContext;
|
const Ctx = server.Cmd;
|
||||||
const SendFn = server.SendFn;
|
|
||||||
const browser = @import("browser.zig").browser;
|
const browser = @import("browser.zig").browser;
|
||||||
const target = @import("target.zig").target;
|
const target = @import("target.zig").target;
|
||||||
|
|
||||||
|
pub const Error = error{
|
||||||
|
UnknonwDomain,
|
||||||
|
UnknownMethod,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn isCdpError(err: anyerror) ?Error {
|
||||||
|
// see https://github.com/ziglang/zig/issues/2473
|
||||||
|
const errors = @typeInfo(Error).ErrorSet.?;
|
||||||
|
inline for (errors) |e| {
|
||||||
|
if (std.mem.eql(u8, e.name, @errorName(err))) {
|
||||||
|
return @errorCast(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const Domains = enum {
|
const Domains = enum {
|
||||||
Browser,
|
Browser,
|
||||||
Target,
|
Target,
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const server = @import("../server.zig");
|
const server = @import("../server.zig");
|
||||||
const Ctx = server.CmdContext;
|
const Ctx = server.Cmd;
|
||||||
const SendFn = server.SendFn;
|
|
||||||
const result = @import("cdp.zig").result;
|
const result = @import("cdp.zig").result;
|
||||||
const getParams = @import("cdp.zig").getParams;
|
const getParams = @import("cdp.zig").getParams;
|
||||||
const stringify = @import("cdp.zig").stringify;
|
const stringify = @import("cdp.zig").stringify;
|
||||||
|
|
||||||
const TargetMethods = enum {
|
const TargetMethods = enum {
|
||||||
setAutoAttach,
|
setAutoAttach,
|
||||||
// attachedToTarget,
|
|
||||||
// getTargetInfo,
|
// getTargetInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -21,10 +19,10 @@ pub fn target(
|
|||||||
ctx: *Ctx,
|
ctx: *Ctx,
|
||||||
) ![]const u8 {
|
) ![]const u8 {
|
||||||
const method = std.meta.stringToEnum(TargetMethods, action) orelse
|
const method = std.meta.stringToEnum(TargetMethods, action) orelse
|
||||||
return error.UnknownTargetMethod;
|
return error.UnknownMethod;
|
||||||
return switch (method) {
|
return switch (method) {
|
||||||
.setAutoAttach => tagetSetAutoAttach(alloc, id, scanner, ctx),
|
.setAutoAttach => tagetSetAutoAttach(alloc, id, scanner, ctx),
|
||||||
// .getTargetInfo => tagetGetTargetInfo(alloc, id, scanner),
|
// .getTargetInfo => tagetGetTargetInfo(alloc, id, scanner, ctx),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +61,7 @@ fn tagetSetAutoAttach(
|
|||||||
} = .{},
|
} = .{},
|
||||||
};
|
};
|
||||||
const attached = try stringify(alloc, AttachToTarget{});
|
const attached = try stringify(alloc, AttachToTarget{});
|
||||||
try server.sendLater(ctx, attached, 0);
|
try server.sendSync(ctx, attached);
|
||||||
|
|
||||||
return result(alloc, id, null, null);
|
return result(alloc, id, null, null);
|
||||||
}
|
}
|
||||||
@@ -93,28 +91,3 @@ fn tagetGetTargetInfo(
|
|||||||
_ = targetInfo;
|
_ = targetInfo;
|
||||||
return result(alloc, id, null, null);
|
return result(alloc, id, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn tagetGetTargetInfo(
|
|
||||||
// alloc: std.mem.Allocator,
|
|
||||||
// id: u64,
|
|
||||||
// scanner: *std.json.Scanner,
|
|
||||||
// ) ![]const u8 {
|
|
||||||
// _ = scanner;
|
|
||||||
|
|
||||||
// const TargetInfo = struct {
|
|
||||||
// targetId: []const u8,
|
|
||||||
// type: []const u8,
|
|
||||||
// title: []const u8,
|
|
||||||
// url: []const u8,
|
|
||||||
// attached: bool,
|
|
||||||
// canAccessOpener: bool,
|
|
||||||
|
|
||||||
// browserContextId: ?[]const u8 = null,
|
|
||||||
// };
|
|
||||||
// const targetInfo = TargetInfo{
|
|
||||||
// .targetId = TargetID,
|
|
||||||
// .type = "page",
|
|
||||||
// };
|
|
||||||
// _ = targetInfo;
|
|
||||||
// return result(alloc, id, null, null);
|
|
||||||
// }
|
|
||||||
|
|||||||
293
src/server.zig
293
src/server.zig
@@ -1,178 +1,165 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const public = @import("jsruntime");
|
const public = @import("jsruntime");
|
||||||
|
const Completion = public.IO.Completion;
|
||||||
|
const AcceptError = public.IO.AcceptError;
|
||||||
|
const RecvError = public.IO.RecvError;
|
||||||
|
const SendError = public.IO.SendError;
|
||||||
|
const TimeoutError = public.IO.TimeoutError;
|
||||||
|
|
||||||
const Window = @import("html/window.zig").Window;
|
const Window = @import("html/window.zig").Window;
|
||||||
|
|
||||||
const cdp = @import("cdp/cdp.zig");
|
const cdp = @import("cdp/cdp.zig");
|
||||||
pub var socket_fd: std.os.socket_t = undefined;
|
pub var socket_fd: std.os.socket_t = undefined;
|
||||||
|
|
||||||
// I/O input command context
|
const NoError = error{NoError};
|
||||||
pub const CmdContext = struct {
|
const Error = AcceptError || RecvError || SendError || TimeoutError || cdp.Error || NoError;
|
||||||
js_env: *public.Env,
|
|
||||||
|
// I/O Recv
|
||||||
|
// --------
|
||||||
|
|
||||||
|
pub const Cmd = struct {
|
||||||
|
|
||||||
|
// internal fields
|
||||||
socket: std.os.socket_t,
|
socket: std.os.socket_t,
|
||||||
completion: *public.IO.Completion,
|
buf: []u8, // only for read operations
|
||||||
|
err: ?Error = null,
|
||||||
read_buf: []u8,
|
|
||||||
write_buf: []const u8 = undefined,
|
|
||||||
|
|
||||||
close: bool = false,
|
|
||||||
|
|
||||||
|
// JS fields
|
||||||
|
js_env: *public.Env,
|
||||||
try_catch: public.TryCatch,
|
try_catch: public.TryCatch,
|
||||||
|
|
||||||
|
fn cbk(self: *Cmd, completion: *Completion, result: RecvError!usize) void {
|
||||||
|
const size = result catch |err| {
|
||||||
|
self.err = err;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const input = self.buf[0..size];
|
||||||
|
|
||||||
|
// close on exit command
|
||||||
|
if (std.mem.eql(u8, input, "exit")) {
|
||||||
|
self.err = error.NoError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// input
|
||||||
|
if (std.log.defaultLogEnabled(.debug)) {
|
||||||
|
std.debug.print("\ninput {s}\n", .{input});
|
||||||
|
}
|
||||||
|
|
||||||
|
// cdp
|
||||||
|
const res = cdp.do(self.alloc(), input, self) catch |err| {
|
||||||
|
if (cdp.isCdpError(err)) |e| {
|
||||||
|
self.err = e;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
@panic(@errorName(err));
|
||||||
|
};
|
||||||
|
std.log.debug("res {s}", .{res});
|
||||||
|
|
||||||
|
sendAsync(self, res) catch unreachable;
|
||||||
|
|
||||||
|
// continue receving incomming messages asynchronously
|
||||||
|
self.loop().io.recv(*Cmd, self, cbk, completion, self.socket, self.buf);
|
||||||
|
}
|
||||||
|
|
||||||
// shortcuts
|
// shortcuts
|
||||||
fn alloc(self: *CmdContext) std.mem.Allocator {
|
fn alloc(self: *Cmd) std.mem.Allocator {
|
||||||
return self.js_env.nat_ctx.alloc;
|
return self.js_env.nat_ctx.alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loop(self: *CmdContext) *public.Loop {
|
fn loop(self: *Cmd) *public.Loop {
|
||||||
return self.js_env.nat_ctx.loop;
|
return self.js_env.nat_ctx.loop;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn respCallback(
|
// I/O Send
|
||||||
ctx: *CmdContext,
|
// --------
|
||||||
_: *public.IO.Completion,
|
|
||||||
result: public.IO.SendError!usize,
|
|
||||||
) void {
|
|
||||||
_ = result catch |err| {
|
|
||||||
ctx.close = true;
|
|
||||||
std.debug.print("send error: {s}\n", .{@errorName(err)});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
std.log.debug("send ok", .{});
|
|
||||||
}
|
|
||||||
|
|
||||||
const SendLaterContext = struct {
|
const Send = struct {
|
||||||
cmd_ctx: *CmdContext,
|
cmd: *Cmd,
|
||||||
completion: *public.IO.Completion,
|
|
||||||
buf: []const u8,
|
buf: []const u8,
|
||||||
};
|
|
||||||
|
|
||||||
fn sendLaterCallback(
|
fn init(ctx: *Cmd, msg: []const u8) !struct {
|
||||||
ctx: *SendLaterContext,
|
ctx: *Send,
|
||||||
completion: *public.IO.Completion,
|
completion: *Completion,
|
||||||
result: public.IO.TimeoutError!void,
|
} {
|
||||||
) void {
|
// NOTE: it seems we can't use the same completion for concurrent
|
||||||
std.log.debug("sending after", .{});
|
// recv and timeout operations, that's why we create a new completion here
|
||||||
_ = result catch |err| {
|
const completion = try ctx.alloc().create(Completion);
|
||||||
ctx.cmd_ctx.close = true;
|
// NOTE: to handle concurrent calls we create each time a new context
|
||||||
std.debug.print("timeout error: {s}\n", .{@errorName(err)});
|
// If no concurrent calls where required we could just use the main CmdCtx
|
||||||
return;
|
const sd = try ctx.alloc().create(Send);
|
||||||
};
|
sd.* = .{
|
||||||
|
.cmd = ctx,
|
||||||
ctx.cmd_ctx.alloc().destroy(completion);
|
.buf = msg,
|
||||||
defer ctx.cmd_ctx.alloc().destroy(ctx);
|
};
|
||||||
send(ctx.cmd_ctx, ctx.buf) catch unreachable;
|
return .{ .ctx = sd, .completion = completion };
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sendLater(ctx: *CmdContext, msg: []const u8, nanoseconds: u63) !void {
|
|
||||||
// NOTE: it seems we can't use the same completion for concurrent
|
|
||||||
// recv and timeout operations, that's why we create a new completion here
|
|
||||||
const completion = try ctx.alloc().create(public.IO.Completion);
|
|
||||||
// NOTE: to handle concurrent calls to sendLater we create each time a new context
|
|
||||||
// If no concurrent calls are required we could just use the main CmdContext
|
|
||||||
const sendLaterCtx = try ctx.alloc().create(SendLaterContext);
|
|
||||||
sendLaterCtx.* = .{
|
|
||||||
.cmd_ctx = ctx,
|
|
||||||
.completion = completion,
|
|
||||||
.buf = msg,
|
|
||||||
};
|
|
||||||
ctx.loop().io.timeout(*SendLaterContext, sendLaterCtx, sendLaterCallback, completion, nanoseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send(ctx: *CmdContext, msg: []const u8) !void {
|
|
||||||
defer ctx.alloc().free(msg);
|
|
||||||
const s = try std.os.write(ctx.socket, msg);
|
|
||||||
std.log.debug("send ok {d}", .{s});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loopSend(ctx: *CmdContext, msg: []const u8) !void {
|
|
||||||
ctx.write_buf = msg;
|
|
||||||
ctx.loop().io.send(
|
|
||||||
*CmdContext,
|
|
||||||
ctx,
|
|
||||||
respCallback,
|
|
||||||
ctx.completion,
|
|
||||||
ctx.socket,
|
|
||||||
ctx.write_buf,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// I/O input command callback
|
|
||||||
fn cmdCallback(
|
|
||||||
ctx: *CmdContext,
|
|
||||||
completion: *public.IO.Completion,
|
|
||||||
result: public.IO.RecvError!usize,
|
|
||||||
) void {
|
|
||||||
// ctx.completion = completion;
|
|
||||||
const size = result catch |err| {
|
|
||||||
ctx.close = true;
|
|
||||||
std.debug.print("recv error: {s}\n", .{@errorName(err)});
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
const input = ctx.read_buf[0..size];
|
|
||||||
|
|
||||||
// close on exit command
|
|
||||||
if (std.mem.eql(u8, input, "exit")) {
|
|
||||||
ctx.close = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.print("\ninput {s}\n", .{input});
|
fn deinit(self: *Send, completion: *Completion) void {
|
||||||
const res = cdp.do(ctx.alloc(), input, ctx) catch |err| {
|
self.cmd.alloc().destroy(completion);
|
||||||
std.log.debug("error: {any}\n", .{err});
|
self.cmd.alloc().free(self.buf);
|
||||||
send(ctx, "{}") catch unreachable;
|
self.cmd.alloc().destroy(self);
|
||||||
// TODO: return proper error
|
}
|
||||||
return;
|
|
||||||
};
|
|
||||||
std.log.debug("res {s}", .{res});
|
|
||||||
|
|
||||||
send(ctx, res) catch unreachable;
|
fn laterCbk(self: *Send, completion: *Completion, result: TimeoutError!void) void {
|
||||||
std.log.debug("finish", .{});
|
std.log.debug("sending after", .{});
|
||||||
|
_ = result catch |err| {
|
||||||
|
self.cmd.err = err;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
// continue receving messages asynchronously
|
self.cmd.loop().io.send(*Send, self, Send.asyncCbk, completion, self.cmd.socket, self.buf);
|
||||||
ctx.loop().io.recv(
|
}
|
||||||
*CmdContext,
|
|
||||||
ctx,
|
|
||||||
cmdCallback,
|
|
||||||
completion,
|
|
||||||
ctx.socket,
|
|
||||||
ctx.read_buf,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// I/O connection context
|
fn asyncCbk(self: *Send, completion: *Completion, result: SendError!usize) void {
|
||||||
const ConnContext = struct {
|
const size = result catch |err| {
|
||||||
socket: std.os.socket_t,
|
self.cmd.err = err;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
cmdContext: *CmdContext,
|
std.log.debug("send async {d} bytes", .{size});
|
||||||
|
self.deinit(completion);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// I/O connection callback
|
pub fn sendLater(ctx: *Cmd, msg: []const u8, ns: u63) !void {
|
||||||
fn connCallback(
|
const sd = try Send.init(ctx, msg);
|
||||||
ctx: *ConnContext,
|
ctx.loop().io.timeout(*Send, sd.ctx, Send.laterCbk, sd.completion, ns);
|
||||||
completion: *public.IO.Completion,
|
|
||||||
result: public.IO.AcceptError!std.os.socket_t,
|
|
||||||
) void {
|
|
||||||
ctx.cmdContext.socket = result catch |err| @panic(@errorName(err));
|
|
||||||
|
|
||||||
// launch receving messages asynchronously
|
|
||||||
ctx.cmdContext.loop().io.recv(
|
|
||||||
*CmdContext,
|
|
||||||
ctx.cmdContext,
|
|
||||||
cmdCallback,
|
|
||||||
completion,
|
|
||||||
ctx.cmdContext.socket,
|
|
||||||
ctx.cmdContext.read_buf,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execJS(
|
pub fn sendAsync(ctx: *Cmd, msg: []const u8) !void {
|
||||||
alloc: std.mem.Allocator,
|
const sd = try Send.init(ctx, msg);
|
||||||
js_env: *public.Env,
|
ctx.loop().io.send(*Send, sd.ctx, Send.asyncCbk, sd.completion, ctx.socket, msg);
|
||||||
) anyerror!void {
|
}
|
||||||
|
|
||||||
|
pub fn sendSync(ctx: *Cmd, msg: []const u8) !void {
|
||||||
|
defer ctx.alloc().free(msg);
|
||||||
|
const s = try std.os.write(ctx.socket, msg);
|
||||||
|
std.log.debug("send sync {d} bytes", .{s});
|
||||||
|
}
|
||||||
|
|
||||||
|
// I/O Accept
|
||||||
|
// ----------
|
||||||
|
|
||||||
|
const Accept = struct {
|
||||||
|
cmd: *Cmd,
|
||||||
|
socket: std.os.socket_t,
|
||||||
|
|
||||||
|
fn cbk(self: *Accept, completion: *Completion, result: AcceptError!std.os.socket_t) void {
|
||||||
|
self.cmd.socket = result catch |err| {
|
||||||
|
self.cmd.err = err;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// receving incomming messages asynchronously
|
||||||
|
self.cmd.loop().io.recv(*Cmd, self.cmd, Cmd.cbk, completion, self.cmd.socket, self.cmd.buf);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn execJS(alloc: std.mem.Allocator, js_env: *public.Env) anyerror!void {
|
||||||
|
|
||||||
// start JS env
|
// start JS env
|
||||||
try js_env.start(alloc);
|
try js_env.start(alloc);
|
||||||
@@ -196,29 +183,22 @@ pub fn execJS(
|
|||||||
|
|
||||||
// create I/O contexts and callbacks
|
// create I/O contexts and callbacks
|
||||||
// for accepting connections and receving messages
|
// for accepting connections and receving messages
|
||||||
var completion: public.IO.Completion = undefined;
|
|
||||||
var input: [1024]u8 = undefined;
|
var input: [1024]u8 = undefined;
|
||||||
var cmd_ctx = CmdContext{
|
var cmd = Cmd{
|
||||||
.js_env = js_env,
|
.js_env = js_env,
|
||||||
.socket = undefined,
|
.socket = undefined,
|
||||||
.read_buf = &input,
|
.buf = &input,
|
||||||
.try_catch = try_catch,
|
.try_catch = try_catch,
|
||||||
.completion = &completion,
|
|
||||||
};
|
};
|
||||||
var conn_ctx = ConnContext{
|
var accept = Accept{
|
||||||
|
.cmd = &cmd,
|
||||||
.socket = socket_fd,
|
.socket = socket_fd,
|
||||||
.cmdContext = &cmd_ctx,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// launch accepting connection asynchronously on internal server
|
// accepting connection asynchronously on internal server
|
||||||
const loop = js_env.nat_ctx.loop;
|
const loop = js_env.nat_ctx.loop;
|
||||||
loop.io.accept(
|
var completion: Completion = undefined;
|
||||||
*ConnContext,
|
loop.io.accept(*Accept, &accept, Accept.cbk, &completion, socket_fd);
|
||||||
&conn_ctx,
|
|
||||||
connCallback,
|
|
||||||
&completion,
|
|
||||||
socket_fd,
|
|
||||||
);
|
|
||||||
|
|
||||||
// infinite loop on I/O events, either:
|
// infinite loop on I/O events, either:
|
||||||
// - cmd from incoming connection on server socket
|
// - cmd from incoming connection on server socket
|
||||||
@@ -232,7 +212,8 @@ pub fn execJS(
|
|||||||
}
|
}
|
||||||
loop.cbk_error = false;
|
loop.cbk_error = false;
|
||||||
}
|
}
|
||||||
if (cmd_ctx.close) {
|
if (cmd.err) |err| {
|
||||||
|
if (err != error.NoError) std.log.err("Server error: {any}", .{err});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user