mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 14:43:28 +00:00
browser: start browser API
This commit is contained in:
23
build.zig
23
build.zig
@@ -115,6 +115,29 @@ pub fn build(b: *std.build.Builder) !void {
|
||||
// step
|
||||
const wpt_step = b.step("wpt", "WPT tests");
|
||||
wpt_step.dependOn(&wpt_cmd.step);
|
||||
|
||||
// get
|
||||
// -----
|
||||
|
||||
// compile and install
|
||||
const get = b.addExecutable(.{
|
||||
.name = "browsercore-get",
|
||||
.root_source_file = .{ .path = "src/main_get.zig" },
|
||||
.target = target,
|
||||
.optimize = mode,
|
||||
});
|
||||
try common(get, options);
|
||||
b.installArtifact(get);
|
||||
|
||||
// run
|
||||
const get_cmd = b.addRunArtifact(get);
|
||||
get_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
get_cmd.addArgs(args);
|
||||
}
|
||||
// step
|
||||
const get_step = b.step("get", "request URL");
|
||||
get_step.dependOn(&get_cmd.step);
|
||||
}
|
||||
|
||||
fn common(
|
||||
|
||||
133
src/browser/browser.zig
Normal file
133
src/browser/browser.zig
Normal file
@@ -0,0 +1,133 @@
|
||||
const std = @import("std");
|
||||
|
||||
const parser = @import("../netsurf.zig");
|
||||
const Loader = @import("loader.zig").Loader;
|
||||
|
||||
const jsruntime = @import("jsruntime");
|
||||
const Loop = jsruntime.Loop;
|
||||
const Env = jsruntime.Env;
|
||||
const TPL = jsruntime.TPL;
|
||||
|
||||
const apiweb = @import("../apiweb.zig");
|
||||
const apis = jsruntime.compile(apiweb.Interfaces);
|
||||
const Window = @import("../nav/window.zig").Window;
|
||||
|
||||
const log = std.log.scoped(.lpd_browser);
|
||||
|
||||
pub const Browser = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
session: Session = undefined,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) Browser {
|
||||
var b = Browser{ .allocator = allocator };
|
||||
b.session = try b.createSession(null);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Browser) void {
|
||||
var session = self.session;
|
||||
session.deinit();
|
||||
}
|
||||
|
||||
pub fn currentSession(self: *Browser) *Session {
|
||||
return &self.session;
|
||||
}
|
||||
|
||||
fn createSession(self: *Browser, uri: ?[]const u8) !Session {
|
||||
return Session.init(self.allocator, uri orelse "about:blank");
|
||||
}
|
||||
};
|
||||
|
||||
pub const Session = struct {
|
||||
allocator: std.mem.Allocator,
|
||||
uri: []const u8,
|
||||
// TODO handle proxy
|
||||
loader: Loader,
|
||||
|
||||
fn init(allocator: std.mem.Allocator, uri: []const u8) Session {
|
||||
return Session{
|
||||
.allocator = allocator,
|
||||
.uri = uri,
|
||||
.loader = Loader.init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *Session) void {
|
||||
self.loader.deinit();
|
||||
}
|
||||
|
||||
pub fn createPage(self: *Session) !Page {
|
||||
return Page.init(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Page = struct {
|
||||
arena: std.heap.ArenaAllocator,
|
||||
session: *Session,
|
||||
|
||||
fn init(session: *Session) Page {
|
||||
return Page{
|
||||
.session = session,
|
||||
.arena = std.heap.ArenaAllocator.init(session.allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Page) void {
|
||||
self.arena.deinit();
|
||||
}
|
||||
|
||||
pub fn navigate(self: *Page, uri: []const u8) !void {
|
||||
const allocator = self.arena.allocator();
|
||||
|
||||
log.debug("starting GET {s}", .{uri});
|
||||
|
||||
// load the data
|
||||
var result = try self.session.loader.fetch(allocator, uri);
|
||||
defer result.deinit();
|
||||
|
||||
log.info("GET {s} {d}", .{ uri, result.status });
|
||||
|
||||
// TODO handle redirection
|
||||
if (result.status != .ok) return error.BadStatusCode;
|
||||
|
||||
if (result.body == null) return error.NoBody;
|
||||
|
||||
// TODO handle charset
|
||||
|
||||
// document
|
||||
const html_doc = try parser.documentHTMLParseFromStrAlloc(allocator, result.body.?);
|
||||
const doc = parser.documentHTMLToDocument(html_doc);
|
||||
|
||||
// create JS env
|
||||
var loop = try Loop.init(allocator);
|
||||
defer loop.deinit();
|
||||
var js_env = try Env.init(allocator, &loop);
|
||||
defer js_env.deinit();
|
||||
|
||||
// load APIs in JS env
|
||||
var tpls: [apis.len]TPL = undefined;
|
||||
try js_env.load(apis, &tpls);
|
||||
|
||||
// start JS env
|
||||
try js_env.start(allocator, apis);
|
||||
defer js_env.stop();
|
||||
|
||||
// add global objects
|
||||
const window = Window.create(doc, null);
|
||||
_ = window;
|
||||
// TODO should'nt we share the same pointer between instances of window?
|
||||
// try js_env.addObject(apis, window, "self");
|
||||
// try js_env.addObject(apis, window, "window");
|
||||
try js_env.addObject(apis, doc, "document");
|
||||
}
|
||||
};
|
||||
|
||||
test "create page" {
|
||||
const allocator = std.testing.allocator;
|
||||
var browser = Browser.init(allocator);
|
||||
defer browser.deinit();
|
||||
|
||||
var page = try browser.currentSession().createPage();
|
||||
defer page.deinit();
|
||||
}
|
||||
54
src/browser/loader.zig
Normal file
54
src/browser/loader.zig
Normal file
@@ -0,0 +1,54 @@
|
||||
const std = @import("std");
|
||||
|
||||
const user_agent = "Lightpanda.io/1.0";
|
||||
|
||||
pub const Loader = struct {
|
||||
client: std.http.Client,
|
||||
|
||||
pub const Response = struct {
|
||||
req: std.http.Request,
|
||||
|
||||
pub fn deinit(self: *Response) void {
|
||||
self.req.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator) Loader {
|
||||
return Loader{
|
||||
.client = std.http.Client{
|
||||
.allocator = allocator,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Loader) void {
|
||||
self.client.deinit();
|
||||
}
|
||||
|
||||
// the caller must deinit the FetchResult.
|
||||
pub fn fetch(self: *Loader, allocator: std.mem.Allocator, uri: []const u8) !std.http.Client.FetchResult {
|
||||
var headers = try std.http.Headers.initList(allocator, &[_]std.http.Field{
|
||||
.{ .name = "User-Agent", .value = user_agent },
|
||||
.{ .name = "Accept", .value = "*/*" },
|
||||
.{ .name = "Accept-Language", .value = "en-US,en;q=0.5" },
|
||||
});
|
||||
defer headers.deinit();
|
||||
|
||||
return try self.client.fetch(allocator, .{
|
||||
.location = .{ .url = uri },
|
||||
.headers = headers,
|
||||
.payload = .none,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
test "basic url fetch" {
|
||||
const alloc = std.testing.allocator;
|
||||
var loader = Loader.init(alloc);
|
||||
defer loader.deinit();
|
||||
|
||||
var result = try loader.fetch(alloc, "https://en.wikipedia.org/wiki/Main_Page");
|
||||
defer result.deinit();
|
||||
|
||||
try std.testing.expect(result.status == std.http.Status.ok);
|
||||
}
|
||||
Reference in New Issue
Block a user