mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
Compare commits
33 Commits
nightly
...
x86-backen
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dabded8d1e | ||
|
|
c3fd0dbf7a | ||
|
|
aeaa745600 | ||
|
|
be27359109 | ||
|
|
e8a2ce3614 | ||
|
|
0559fb9365 | ||
|
|
89f898cfa9 | ||
|
|
183abc4610 | ||
|
|
5dfdedea0e | ||
|
|
8d89c6053e | ||
|
|
58d184dba1 | ||
|
|
fd0813fead | ||
|
|
c9fae67649 | ||
|
|
3e7f9aaa82 | ||
|
|
22b03bf7df | ||
|
|
6a4d64ed00 | ||
|
|
df27ce09ca | ||
|
|
6da2954e0b | ||
|
|
95245229b0 | ||
|
|
65c4b471af | ||
|
|
d6e0559efd | ||
|
|
5f5e01a2cf | ||
|
|
8c3939b842 | ||
|
|
b537e52a6d | ||
|
|
4434e11bdd | ||
|
|
b8ec53f708 | ||
|
|
f8395fec5c | ||
|
|
28155cb8d3 | ||
|
|
f793278dfe | ||
|
|
cdbbc71b0a | ||
|
|
bfa6f55551 | ||
|
|
5fb25cf015 | ||
|
|
5cad450e5a |
62
.github/actions/install/action.yml
vendored
62
.github/actions/install/action.yml
vendored
@@ -1,23 +1,73 @@
|
||||
name: "Browsercore install"
|
||||
description: "Install deps for the project browsercore"
|
||||
|
||||
inputs:
|
||||
zig:
|
||||
description: 'Zig version to install'
|
||||
required: false
|
||||
default: '0.13.0'
|
||||
arch:
|
||||
description: 'CPU arch used to select the v8 lib'
|
||||
required: false
|
||||
default: 'x86_64'
|
||||
os:
|
||||
description: 'OS used to select the v8 lib'
|
||||
required: false
|
||||
default: 'linux'
|
||||
zig-v8:
|
||||
description: 'zig v8 version to install'
|
||||
required: false
|
||||
default: 'v0.1.6'
|
||||
v8:
|
||||
description: 'v8 version to install'
|
||||
required: false
|
||||
default: '11.1.134'
|
||||
cache-dir:
|
||||
description: 'cache dir to use'
|
||||
required: false
|
||||
default: '~/.cache'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
|
||||
steps:
|
||||
- name: Install apt deps
|
||||
if: ${{ inputs.os == 'linux' }}
|
||||
shell: bash
|
||||
run: sudo apt-get install -y wget xz-utils python3 ca-certificates git pkg-config libglib2.0-dev gperf libexpat1-dev cmake clang
|
||||
|
||||
- uses: mlugg/setup-zig@v1
|
||||
with:
|
||||
version: ${{ inputs.zig }}
|
||||
|
||||
- name: Cache v8
|
||||
id: cache-v8
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: cache-v8
|
||||
with:
|
||||
path: ${{ inputs.cache-dir }}/v8
|
||||
key: libc_v8_${{ inputs.v8 }}_${{ inputs.os }}_${{ inputs.arch }}.a
|
||||
|
||||
- if: ${{ steps.cache-v8.outputs.cache-hit != 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p ${{ inputs.cache-dir }}/v8
|
||||
|
||||
wget -O ${{ inputs.cache-dir }}/v8/libc_v8.a https://github.com/lightpanda-io/zig-v8-fork/releases/download/${{ inputs.zig-v8 }}/libc_v8_${{ inputs.v8 }}_${{ inputs.os }}_${{ inputs.arch }}.a
|
||||
|
||||
- name: install v8
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p vendor/zig-js-runtime/vendor/v8/${{env.ARCH}}/debug
|
||||
ln -s /usr/local/lib/libc_v8.a vendor/zig-js-runtime/vendor/v8/${{env.ARCH}}/debug/libc_v8.a
|
||||
mkdir -p vendor/zig-js-runtime/vendor/v8/${{inputs.arch}}-${{inputs.os}}/debug
|
||||
ln -s ${{ inputs.cache-dir }}/v8/libc_v8.a vendor/zig-js-runtime/vendor/v8/${{inputs.arch}}-${{inputs.os}}/debug/libc_v8.a
|
||||
|
||||
mkdir -p vendor/zig-js-runtime/vendor/v8/${{env.ARCH}}/release
|
||||
ln -s /usr/local/lib/libc_v8.a vendor/zig-js-runtime/vendor/v8/${{env.ARCH}}/release/libc_v8.a
|
||||
mkdir -p vendor/zig-js-runtime/vendor/v8/${{inputs.arch}}-${{inputs.os}}/release
|
||||
ln -s ${{ inputs.cache-dir }}/v8/libc_v8.a vendor/zig-js-runtime/vendor/v8/${{inputs.arch}}-${{inputs.os}}/release/libc_v8.a
|
||||
|
||||
- name: libiconv
|
||||
shell: bash
|
||||
run: |
|
||||
ln -s /usr/local/lib/libiconv vendor/libiconv
|
||||
run: make install-libiconv
|
||||
|
||||
- name: build mimalloc
|
||||
shell: bash
|
||||
|
||||
75
.github/workflows/build.yml
vendored
Normal file
75
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
name: nightly build
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "2 2 * * *"
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-linux-x86_64:
|
||||
env:
|
||||
ARCH: x86_64
|
||||
OS: linux
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GH_CI_PAT }}
|
||||
# fetch submodules recusively, to get zig-js-runtime submodules also.
|
||||
submodules: recursive
|
||||
|
||||
- uses: ./.github/actions/install
|
||||
|
||||
- name: zig build
|
||||
run: zig build --release=safe -Doptimize=ReleaseSafe -Dengine=v8 -Dcpu=x86_64
|
||||
|
||||
- name: Rename binary
|
||||
run: mv zig-out/bin/browsercore-get lightpanda-get-${{ env.ARCH }}-${{ env.OS }}
|
||||
|
||||
- name: Upload the build
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
allowUpdates: true
|
||||
artifacts: lightpanda-get-${{ env.ARCH }}-${{ env.OS }}
|
||||
tag: nightly
|
||||
|
||||
build-macos-aarch64:
|
||||
env:
|
||||
ARCH: aarch64
|
||||
OS: macos
|
||||
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GH_CI_PAT }}
|
||||
# fetch submodules recusively, to get zig-js-runtime submodules also.
|
||||
submodules: recursive
|
||||
|
||||
- uses: ./.github/actions/install
|
||||
with:
|
||||
os: ${{env.OS}}
|
||||
arch: ${{env.ARCH}}
|
||||
|
||||
- name: zig build
|
||||
run: zig build --release=safe -Doptimize=ReleaseSafe -Dengine=v8
|
||||
|
||||
- name: Rename binary
|
||||
run: mv zig-out/bin/browsercore-get lightpanda-get-${{ env.ARCH }}-${{ env.OS }}
|
||||
|
||||
- name: Upload the build
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
allowUpdates: true
|
||||
artifacts: lightpanda-get-${{ env.ARCH }}-${{ env.OS }}
|
||||
tag: nightly
|
||||
11
.github/workflows/wpt.yml
vendored
11
.github/workflows/wpt.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: wpt
|
||||
|
||||
env:
|
||||
ARCH: x86_64-linux
|
||||
AWS_ACCESS_KEY_ID: ${{ vars.LPD_PERF_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.LPD_PERF_AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_BUCKET: ${{ vars.LPD_PERF_AWS_BUCKET }}
|
||||
@@ -46,16 +45,6 @@ jobs:
|
||||
if: github.event.pull_request.draft == false
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/lightpanda-io/zig-browsercore:0.12.1
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# docker blocks io_uring syscalls by default now.
|
||||
# see https://github.com/tigerbeetle/tigerbeetle/pull/1995
|
||||
# see https://github.com/moby/moby/pull/46762
|
||||
options: "--security-opt seccomp=unconfined"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
14
.github/workflows/zig-fmt.yml
vendored
14
.github/workflows/zig-fmt.yml
vendored
@@ -1,5 +1,8 @@
|
||||
name: zig-fmt
|
||||
|
||||
env:
|
||||
ZIG_VERSION: 0.13.0
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
@@ -26,15 +29,12 @@ jobs:
|
||||
if: github.event.pull_request.draft == false
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/lightpanda-io/zig:0.12.1
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
outputs:
|
||||
zig_fmt_errs: ${{ steps.fmt.outputs.zig_fmt_errs }}
|
||||
|
||||
steps:
|
||||
- uses: mlugg/setup-zig@v1
|
||||
with:
|
||||
version: ${{ env.ZIG_VERSION }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
21
.github/workflows/zig-test.yml
vendored
21
.github/workflows/zig-test.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: zig-test
|
||||
|
||||
env:
|
||||
ARCH: x86_64-linux
|
||||
AWS_ACCESS_KEY_ID: ${{ vars.LPD_PERF_AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.LPD_PERF_AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_BUCKET: ${{ vars.LPD_PERF_AWS_BUCKET }}
|
||||
@@ -44,11 +43,6 @@ jobs:
|
||||
if: github.event.pull_request.draft == false
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/lightpanda-io/zig-browsercore:0.12.1
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -70,11 +64,6 @@ jobs:
|
||||
if: github.event_name != 'pull_request'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/lightpanda-io/zig-browsercore:0.12.1
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -96,16 +85,6 @@ jobs:
|
||||
if: github.event.pull_request.draft == false
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ghcr.io/lightpanda-io/zig-browsercore:0.12.1
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# docker blocks io_uring syscalls by default now.
|
||||
# see https://github.com/tigerbeetle/tigerbeetle/pull/1995
|
||||
# see https://github.com/moby/moby/pull/46762
|
||||
options: "--security-opt seccomp=unconfined"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
zig-cache
|
||||
/.zig-cache/
|
||||
zig-out
|
||||
/vendor/netsurf/build/
|
||||
/vendor/netsurf/lib/
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -22,3 +22,6 @@
|
||||
[submodule "vendor/mimalloc"]
|
||||
path = vendor/mimalloc
|
||||
url = git@github.com:microsoft/mimalloc.git
|
||||
[submodule "vendor/tls.zig"]
|
||||
path = vendor/tls.zig
|
||||
url = git@github.com:ianic/tls.zig.git
|
||||
|
||||
@@ -76,7 +76,7 @@ We do not provide yet binary versions of Lightpanda, you have to compile it from
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Lightpanda is written with [Zig](https://ziglang.org/) `0.12.1`. You have to
|
||||
Lightpanda is written with [Zig](https://ziglang.org/) `0.13.0`. You have to
|
||||
install it with the right version in order to build the project.
|
||||
|
||||
Lightpanda also depends on
|
||||
|
||||
17
build.zig
17
build.zig
@@ -47,6 +47,8 @@ pub fn build(b: *std.Build) !void {
|
||||
|
||||
const options = try jsruntime.buildOptions(b);
|
||||
|
||||
const x86 = b.option(bool, "x86", "Use x86 backend") orelse false;
|
||||
|
||||
// browser
|
||||
// -------
|
||||
|
||||
@@ -56,6 +58,8 @@ pub fn build(b: *std.Build) !void {
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = mode,
|
||||
.use_llvm = !x86,
|
||||
.use_lld = !x86,
|
||||
});
|
||||
try common(b, exe, options);
|
||||
b.installArtifact(exe);
|
||||
@@ -79,6 +83,8 @@ pub fn build(b: *std.Build) !void {
|
||||
.root_source_file = b.path("src/main_shell.zig"),
|
||||
.target = target,
|
||||
.optimize = mode,
|
||||
.use_llvm = !x86,
|
||||
.use_lld = !x86,
|
||||
});
|
||||
try common(b, shell, options);
|
||||
try jsruntime_pkgs.add_shell(shell);
|
||||
@@ -102,6 +108,8 @@ pub fn build(b: *std.Build) !void {
|
||||
.test_runner = b.path("src/test_runner.zig"),
|
||||
.target = target,
|
||||
.optimize = mode,
|
||||
.use_llvm = !x86,
|
||||
.use_lld = !x86,
|
||||
});
|
||||
try common(b, tests, options);
|
||||
|
||||
@@ -128,6 +136,8 @@ pub fn build(b: *std.Build) !void {
|
||||
.root_source_file = b.path("src/main_wpt.zig"),
|
||||
.target = target,
|
||||
.optimize = mode,
|
||||
.use_llvm = !x86,
|
||||
.use_lld = !x86,
|
||||
});
|
||||
try common(b, wpt, options);
|
||||
|
||||
@@ -149,6 +159,8 @@ pub fn build(b: *std.Build) !void {
|
||||
.root_source_file = b.path("src/main_get.zig"),
|
||||
.target = target,
|
||||
.optimize = mode,
|
||||
.use_llvm = !x86,
|
||||
.use_lld = !x86,
|
||||
});
|
||||
try common(b, get, options);
|
||||
b.installArtifact(get);
|
||||
@@ -179,6 +191,11 @@ fn common(
|
||||
const netsurf = moduleNetSurf(b);
|
||||
netsurf.addImport("jsruntime", jsruntimemod);
|
||||
step.root_module.addImport("netsurf", netsurf);
|
||||
|
||||
const tlsmod = b.addModule("tls", .{
|
||||
.root_source_file = b.path("vendor/tls.zig/src/main.zig"),
|
||||
});
|
||||
step.root_module.addImport("tls", tlsmod);
|
||||
}
|
||||
|
||||
fn moduleNetSurf(b: *std.Build) *std.Build.Module {
|
||||
|
||||
@@ -35,7 +35,9 @@ const assert = std.debug.assert;
|
||||
const use_vectors = builtin.zig_backend != .stage2_x86_64;
|
||||
|
||||
const Client = @This();
|
||||
const proto = http.protocol;
|
||||
const proto = std.http.protocol;
|
||||
|
||||
const tls23 = @import("tls");
|
||||
|
||||
const Loop = @import("jsruntime").Loop;
|
||||
const tcp = @import("tcp.zig");
|
||||
@@ -217,7 +219,7 @@ pub const ConnectionPool = struct {
|
||||
pub const Connection = struct {
|
||||
stream: Stream,
|
||||
/// undefined unless protocol is tls.
|
||||
tls_client: if (!disable_tls) *std.crypto.tls.Client else void,
|
||||
tls_client: if (!disable_tls) *tls23.Connection(Stream) else void,
|
||||
|
||||
/// The protocol that this connection is using.
|
||||
protocol: Protocol,
|
||||
@@ -246,12 +248,12 @@ pub const Connection = struct {
|
||||
pub const Protocol = enum { plain, tls };
|
||||
|
||||
pub fn readvDirectTls(conn: *Connection, buffers: []std.posix.iovec) ReadError!usize {
|
||||
return conn.tls_client.readv(conn.stream, buffers) catch |err| {
|
||||
return conn.tls_client.readv(buffers) catch |err| {
|
||||
// https://github.com/ziglang/zig/issues/2473
|
||||
if (mem.startsWith(u8, @errorName(err), "TlsAlert")) return error.TlsAlert;
|
||||
|
||||
switch (err) {
|
||||
error.TlsConnectionTruncated, error.TlsRecordOverflow, error.TlsDecodeError, error.TlsBadRecordMac, error.TlsBadLength, error.TlsIllegalParameter, error.TlsUnexpectedMessage => return error.TlsFailure,
|
||||
error.TlsRecordOverflow, error.TlsBadRecordMac, error.TlsUnexpectedMessage => return error.TlsFailure,
|
||||
error.ConnectionTimedOut => return error.ConnectionTimedOut,
|
||||
error.ConnectionResetByPeer, error.BrokenPipe => return error.ConnectionResetByPeer,
|
||||
else => return error.UnexpectedReadFailure,
|
||||
@@ -278,7 +280,7 @@ pub const Connection = struct {
|
||||
if (conn.read_end != conn.read_start) return;
|
||||
|
||||
var iovecs = [1]std.posix.iovec{
|
||||
.{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len },
|
||||
.{ .base = &conn.read_buf, .len = conn.read_buf.len },
|
||||
};
|
||||
const nread = try conn.readvDirect(&iovecs);
|
||||
if (nread == 0) return error.EndOfStream;
|
||||
@@ -314,8 +316,8 @@ pub const Connection = struct {
|
||||
}
|
||||
|
||||
var iovecs = [2]std.posix.iovec{
|
||||
.{ .iov_base = buffer.ptr, .iov_len = buffer.len },
|
||||
.{ .iov_base = &conn.read_buf, .iov_len = conn.read_buf.len },
|
||||
.{ .base = buffer.ptr, .len = buffer.len },
|
||||
.{ .base = &conn.read_buf, .len = conn.read_buf.len },
|
||||
};
|
||||
const nread = try conn.readvDirect(&iovecs);
|
||||
|
||||
@@ -344,7 +346,7 @@ pub const Connection = struct {
|
||||
}
|
||||
|
||||
pub fn writeAllDirectTls(conn: *Connection, buffer: []const u8) WriteError!void {
|
||||
return conn.tls_client.writeAll(conn.stream, buffer) catch |err| switch (err) {
|
||||
return conn.tls_client.writeAll(buffer) catch |err| switch (err) {
|
||||
error.BrokenPipe, error.ConnectionResetByPeer => return error.ConnectionResetByPeer,
|
||||
else => return error.UnexpectedWriteFailure,
|
||||
};
|
||||
@@ -412,7 +414,7 @@ pub const Connection = struct {
|
||||
if (disable_tls) unreachable;
|
||||
|
||||
// try to cleanly close the TLS connection, for any server that cares.
|
||||
_ = conn.tls_client.writeEnd(conn.stream, "", true) catch {};
|
||||
conn.tls_client.close() catch {};
|
||||
allocator.destroy(conn.tls_client);
|
||||
}
|
||||
|
||||
@@ -1376,13 +1378,13 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec
|
||||
if (protocol == .tls) {
|
||||
if (disable_tls) unreachable;
|
||||
|
||||
conn.data.tls_client = try client.allocator.create(std.crypto.tls.Client);
|
||||
conn.data.tls_client = try client.allocator.create(tls23.Connection(Stream));
|
||||
errdefer client.allocator.destroy(conn.data.tls_client);
|
||||
|
||||
conn.data.tls_client.* = std.crypto.tls.Client.init(stream, client.ca_bundle, host) catch return error.TlsInitializationFailed;
|
||||
// This is appropriate for HTTPS because the HTTP headers contain
|
||||
// the content length which is used to detect truncation attacks.
|
||||
conn.data.tls_client.allow_truncation_attacks = true;
|
||||
conn.data.tls_client.* = tls23.client(stream, .{
|
||||
.host = host,
|
||||
.root_ca = client.ca_bundle,
|
||||
}) catch return error.TlsInitializationFailed;
|
||||
}
|
||||
|
||||
client.connection_pool.addUsed(conn);
|
||||
@@ -1560,7 +1562,7 @@ pub const RequestOptions = struct {
|
||||
};
|
||||
|
||||
fn validateUri(uri: Uri, arena: Allocator) !struct { Connection.Protocol, Uri } {
|
||||
const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{
|
||||
const protocol_map = std.StaticStringMap(Connection.Protocol).initComptime(.{
|
||||
.{ "http", .plain },
|
||||
.{ "ws", .plain },
|
||||
.{ "https", .tls },
|
||||
|
||||
@@ -107,7 +107,7 @@ pub const Stream = struct {
|
||||
/// See equivalent function: `std.fs.File.writev`.
|
||||
pub fn writev(self: Stream, iovecs: []const posix.iovec_const) WriteError!usize {
|
||||
if (iovecs.len == 0) return 0;
|
||||
const first_buffer = iovecs[0].iov_base[0..iovecs[0].iov_len];
|
||||
const first_buffer = iovecs[0].base[0..iovecs[0].len];
|
||||
return try self.write(first_buffer);
|
||||
}
|
||||
|
||||
@@ -121,13 +121,13 @@ pub const Stream = struct {
|
||||
var i: usize = 0;
|
||||
while (true) {
|
||||
var amt = try self.writev(iovecs[i..]);
|
||||
while (amt >= iovecs[i].iov_len) {
|
||||
amt -= iovecs[i].iov_len;
|
||||
while (amt >= iovecs[i].len) {
|
||||
amt -= iovecs[i].len;
|
||||
i += 1;
|
||||
if (i >= iovecs.len) return;
|
||||
}
|
||||
iovecs[i].iov_base += amt;
|
||||
iovecs[i].iov_len -= amt;
|
||||
iovecs[i].base += amt;
|
||||
iovecs[i].len -= amt;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ const Walker = @import("../dom/walker.zig").WalkerDepthFirst;
|
||||
|
||||
const storage = @import("../storage/storage.zig");
|
||||
|
||||
const FetchResult = std.http.Client.FetchResult;
|
||||
const FetchResult = @import("../http/Client.zig").Client.FetchResult;
|
||||
|
||||
const UserContext = @import("../user_context.zig").UserContext;
|
||||
const HttpClient = @import("../async/Client.zig");
|
||||
@@ -125,10 +125,10 @@ pub const Session = struct {
|
||||
self.env.deinit();
|
||||
self.arena.deinit();
|
||||
|
||||
self.loader.deinit();
|
||||
self.loop.deinit();
|
||||
self.storageShed.deinit();
|
||||
self.httpClient.deinit();
|
||||
self.loader.deinit();
|
||||
self.storageShed.deinit();
|
||||
self.loop.deinit();
|
||||
self.alloc.destroy(self);
|
||||
}
|
||||
|
||||
@@ -198,22 +198,25 @@ pub const Page = struct {
|
||||
}
|
||||
|
||||
pub fn wait(self: *Page) !void {
|
||||
|
||||
// try catch
|
||||
var try_catch: jsruntime.TryCatch = undefined;
|
||||
try_catch.init(self.session.env);
|
||||
defer try_catch.deinit();
|
||||
|
||||
self.session.env.wait() catch |err| {
|
||||
// the js env could not be started if the document wasn't an HTML.
|
||||
if (err == error.EnvNotStarted) return;
|
||||
|
||||
const alloc = self.arena.allocator();
|
||||
var res = try self.session.env.waitTryCatch(alloc);
|
||||
defer res.deinit(alloc);
|
||||
|
||||
if (res.success) {
|
||||
log.debug("wait: {s}", .{res.result});
|
||||
} else {
|
||||
if (builtin.mode == .Debug and res.stack != null) {
|
||||
log.info("wait: {s}", .{res.stack.?});
|
||||
} else {
|
||||
log.info("wait: {s}", .{res.result});
|
||||
}
|
||||
}
|
||||
|
||||
if (try try_catch.err(alloc, self.session.env)) |msg| {
|
||||
defer alloc.free(msg);
|
||||
log.info("wait error: {s}", .{msg});
|
||||
return;
|
||||
}
|
||||
};
|
||||
log.debug("wait: OK", .{});
|
||||
}
|
||||
|
||||
// spec reference: https://html.spec.whatwg.org/#document-lifecycle
|
||||
pub fn navigate(self: *Page, uri: []const u8) !void {
|
||||
@@ -322,7 +325,7 @@ pub const Page = struct {
|
||||
// start JS env
|
||||
// TODO load the js env concurrently with the HTML parsing.
|
||||
log.debug("start js env", .{});
|
||||
try self.session.env.start(alloc);
|
||||
try self.session.env.start();
|
||||
|
||||
// replace the user context document with the new one.
|
||||
try self.session.env.setUserContext(.{
|
||||
@@ -473,22 +476,26 @@ pub const Page = struct {
|
||||
return;
|
||||
}
|
||||
|
||||
var try_catch: jsruntime.TryCatch = undefined;
|
||||
try_catch.init(self.session.env);
|
||||
defer try_catch.deinit();
|
||||
|
||||
const opt_text = try parser.nodeTextContent(parser.elementToNode(e));
|
||||
if (opt_text) |text| {
|
||||
// TODO handle charset attribute
|
||||
var res = try self.session.env.execTryCatch(alloc, text, "");
|
||||
defer res.deinit(alloc);
|
||||
|
||||
if (res.success) {
|
||||
log.debug("eval inline: {s}", .{res.result});
|
||||
} else {
|
||||
if (builtin.mode == .Debug and res.stack != null) {
|
||||
log.info("eval inline: {s}", .{res.stack.?});
|
||||
} else {
|
||||
log.info("eval inline: {s}", .{res.result});
|
||||
}
|
||||
const res = self.session.env.exec(text, "") catch {
|
||||
if (try try_catch.err(alloc, self.session.env)) |msg| {
|
||||
defer alloc.free(msg);
|
||||
log.info("eval inline {s}: {s}", .{ text, msg });
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
if (builtin.mode == .Debug) {
|
||||
const msg = try res.toString(alloc, self.session.env);
|
||||
defer alloc.free(msg);
|
||||
log.debug("eval inline {s}", .{msg});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -530,18 +537,22 @@ pub const Page = struct {
|
||||
// check no body
|
||||
if (body.len == 0) return FetchError.NoBody;
|
||||
|
||||
var res = try self.session.env.execTryCatch(alloc, body, src);
|
||||
defer res.deinit(alloc);
|
||||
var try_catch: jsruntime.TryCatch = undefined;
|
||||
try_catch.init(self.session.env);
|
||||
defer try_catch.deinit();
|
||||
|
||||
if (res.success) {
|
||||
log.debug("eval remote {s}: {s}", .{ src, res.result });
|
||||
} else {
|
||||
if (builtin.mode == .Debug and res.stack != null) {
|
||||
log.info("eval remote {s}: {s}", .{ src, res.stack.? });
|
||||
} else {
|
||||
log.info("eval remote {s}: {s}", .{ src, res.result });
|
||||
const res = self.session.env.exec(body, src) catch {
|
||||
if (try try_catch.err(alloc, self.session.env)) |msg| {
|
||||
defer alloc.free(msg);
|
||||
log.info("eval remote {s}: {s}", .{ src, msg });
|
||||
}
|
||||
return FetchError.JsErr;
|
||||
};
|
||||
|
||||
if (builtin.mode == .Debug) {
|
||||
const msg = try res.toString(alloc, self.session.env);
|
||||
defer alloc.free(msg);
|
||||
log.debug("eval remote {s}: {s}", .{ src, msg });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,17 +17,18 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const Client = @import("../http/Client.zig");
|
||||
|
||||
const user_agent = "Lightpanda.io/1.0";
|
||||
|
||||
pub const Loader = struct {
|
||||
client: std.http.Client,
|
||||
// use 16KB for headers buffer size.
|
||||
server_header_buffer: [1024 * 16]u8 = undefined,
|
||||
client: Client,
|
||||
// use 64KB for headers buffer size.
|
||||
server_header_buffer: [1024 * 64]u8 = undefined,
|
||||
|
||||
pub const Response = struct {
|
||||
alloc: std.mem.Allocator,
|
||||
req: *std.http.Client.Request,
|
||||
req: *Client.Request,
|
||||
|
||||
pub fn deinit(self: *Response) void {
|
||||
self.req.deinit();
|
||||
@@ -37,7 +38,7 @@ pub const Loader = struct {
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) Loader {
|
||||
return Loader{
|
||||
.client = std.http.Client{
|
||||
.client = Client{
|
||||
.allocator = alloc,
|
||||
},
|
||||
};
|
||||
@@ -54,7 +55,7 @@ pub const Loader = struct {
|
||||
pub fn get(self: *Loader, alloc: std.mem.Allocator, uri: std.Uri) !Response {
|
||||
var resp = Response{
|
||||
.alloc = alloc,
|
||||
.req = try alloc.create(std.http.Client.Request),
|
||||
.req = try alloc.create(Client.Request),
|
||||
};
|
||||
errdefer alloc.destroy(resp.req);
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
const std = @import("std");
|
||||
|
||||
const parser = @import("netsurf");
|
||||
const jsruntime = @import("jsruntime");
|
||||
const Callback = jsruntime.Callback;
|
||||
const CallbackArg = jsruntime.CallbackArg;
|
||||
const Loop = jsruntime.Loop;
|
||||
|
||||
const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
||||
|
||||
@@ -39,6 +43,11 @@ pub const Window = struct {
|
||||
|
||||
storageShelf: ?*storage.Shelf = null,
|
||||
|
||||
// store a map between internal timeouts ids and pointers to uint.
|
||||
// the maximum number of possible timeouts is fixed.
|
||||
timeoutid: u32 = 0,
|
||||
timeoutids: [512]u64 = undefined,
|
||||
|
||||
pub fn create(target: ?[]const u8) Window {
|
||||
return Window{
|
||||
.target = target orelse "",
|
||||
@@ -82,4 +91,26 @@ pub const Window = struct {
|
||||
if (self.storageShelf == null) return parser.DOMError.NotSupported;
|
||||
return &self.storageShelf.?.bucket.session;
|
||||
}
|
||||
|
||||
// TODO handle callback arguments.
|
||||
pub fn _setTimeout(self: *Window, loop: *Loop, cbk: Callback, delay: ?u32) !u32 {
|
||||
if (self.timeoutid >= self.timeoutids.len) return error.TooMuchTimeout;
|
||||
|
||||
const ddelay: u63 = delay orelse 0;
|
||||
const id = loop.timeout(ddelay * std.time.ns_per_ms, cbk);
|
||||
|
||||
self.timeoutids[self.timeoutid] = id;
|
||||
defer self.timeoutid += 1;
|
||||
|
||||
return self.timeoutid;
|
||||
}
|
||||
|
||||
pub fn _clearTimeout(self: *Window, loop: *Loop, id: u32) void {
|
||||
// I do would prefer return an error in this case, but it seems some JS
|
||||
// uses invalid id, in particular id 0.
|
||||
// So we silently ignore invalid id for now.
|
||||
if (id >= self.timeoutid) return;
|
||||
|
||||
loop.cancel(self.timeoutids[id], null);
|
||||
}
|
||||
};
|
||||
|
||||
1794
src/http/Client.zig
Normal file
1794
src/http/Client.zig
Normal file
File diff suppressed because it is too large
Load Diff
18
src/main.zig
18
src/main.zig
@@ -37,7 +37,7 @@ fn execJS(
|
||||
js_env: *jsruntime.Env,
|
||||
) anyerror!void {
|
||||
// start JS env
|
||||
try js_env.start(alloc);
|
||||
try js_env.start();
|
||||
defer js_env.stop();
|
||||
|
||||
// alias global as self and window
|
||||
@@ -45,6 +45,11 @@ fn execJS(
|
||||
window.replaceDocument(doc);
|
||||
try js_env.bindGlobal(window);
|
||||
|
||||
// try catch
|
||||
var try_catch: jsruntime.TryCatch = undefined;
|
||||
try_catch.init(js_env.*);
|
||||
defer try_catch.deinit();
|
||||
|
||||
while (true) {
|
||||
|
||||
// read cmd
|
||||
@@ -57,11 +62,12 @@ fn execJS(
|
||||
break;
|
||||
}
|
||||
|
||||
const res = try js_env.execTryCatch(alloc, cmd, "cdp");
|
||||
if (res.success) {
|
||||
std.debug.print("-> {s}\n", .{res.result});
|
||||
}
|
||||
_ = try conn.stream.write(res.result);
|
||||
const res = try js_env.exec(cmd, "cdp");
|
||||
const res_str = try res.toString(alloc, js_env.*);
|
||||
defer alloc.free(res_str);
|
||||
std.debug.print("-> {s}\n", .{res_str});
|
||||
|
||||
_ = try conn.stream.write(res_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ fn execJS(
|
||||
js_env: *jsruntime.Env,
|
||||
) anyerror!void {
|
||||
// start JS env
|
||||
try js_env.start(alloc);
|
||||
try js_env.start();
|
||||
defer js_env.stop();
|
||||
|
||||
var cli = Client{ .allocator = alloc, .loop = js_env.nat_ctx.loop };
|
||||
|
||||
@@ -142,7 +142,7 @@ pub fn main() !void {
|
||||
defer arena.deinit();
|
||||
|
||||
const res = wpt.run(&arena, wpt_dir, tc, &loader) catch |err| {
|
||||
const suite = try Suite.init(alloc, tc, false, @errorName(err), null);
|
||||
const suite = try Suite.init(alloc, tc, false, @errorName(err));
|
||||
try results.append(suite);
|
||||
|
||||
if (out == .text) {
|
||||
@@ -151,9 +151,9 @@ pub fn main() !void {
|
||||
failures += 1;
|
||||
continue;
|
||||
};
|
||||
// no need to call res.deinit() thanks to the arena allocator.
|
||||
defer res.deinit(arena.allocator());
|
||||
|
||||
const suite = try Suite.init(alloc, tc, res.success, res.result, res.stack);
|
||||
const suite = try Suite.init(alloc, tc, res.ok, res.msg orelse "");
|
||||
try results.append(suite);
|
||||
|
||||
if (out == .json) {
|
||||
@@ -196,7 +196,7 @@ pub fn main() !void {
|
||||
try cases.append(Case{
|
||||
.pass = suite.pass,
|
||||
.name = suite.name,
|
||||
.message = suite.stack orelse suite.message,
|
||||
.message = suite.message,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ fn runSafe(
|
||||
argv.appendAssumeCapacity(tc);
|
||||
defer _ = argv.pop();
|
||||
|
||||
const run = try std.ChildProcess.run(.{
|
||||
const run = try std.process.Child.run(.{
|
||||
.allocator = alloc,
|
||||
.argv = argv.items,
|
||||
.max_output_bytes = 1024 * 1024,
|
||||
|
||||
@@ -71,7 +71,7 @@ fn testExecFn(
|
||||
defer parser.deinit();
|
||||
|
||||
// start JS env
|
||||
try js_env.start(alloc);
|
||||
try js_env.start();
|
||||
defer js_env.stop();
|
||||
|
||||
var storageShelf = storage.Shelf.init(alloc);
|
||||
|
||||
@@ -36,7 +36,7 @@ const Client = @import("../async/Client.zig");
|
||||
// runWPT parses the given HTML file, starts a js env and run the first script
|
||||
// tags containing javascript sources.
|
||||
// It loads first the js libs files.
|
||||
pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const u8, loader: *FileLoader) !jsruntime.JSResult {
|
||||
pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const u8, loader: *FileLoader) !Res {
|
||||
const alloc = arena.allocator();
|
||||
try parser.init();
|
||||
defer parser.deinit();
|
||||
@@ -70,15 +70,16 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const
|
||||
try js_env.load(&js_types);
|
||||
|
||||
// start JS env
|
||||
try js_env.start(alloc);
|
||||
try js_env.start();
|
||||
defer js_env.stop();
|
||||
|
||||
// display console logs
|
||||
defer {
|
||||
var res = evalJS(js_env, alloc, "console.join('\\n');", "console") catch unreachable;
|
||||
const res = evalJS(js_env, alloc, "console.join('\\n');", "console") catch unreachable;
|
||||
defer res.deinit(alloc);
|
||||
if (res.result.len > 0) {
|
||||
std.debug.print("-- CONSOLE LOG\n{s}\n--\n", .{res.result});
|
||||
|
||||
if (res.msg != null and res.msg.?.len > 0) {
|
||||
std.debug.print("-- CONSOLE LOG\n{s}\n--\n", .{res.msg.?});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,9 +89,6 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const
|
||||
window.setStorageShelf(&storageShelf);
|
||||
try js_env.bindGlobal(&window);
|
||||
|
||||
// thanks to the arena, we don't need to deinit res.
|
||||
var res: jsruntime.JSResult = undefined;
|
||||
|
||||
const init =
|
||||
\\console = [];
|
||||
\\console.log = function () {
|
||||
@@ -100,10 +98,8 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const
|
||||
\\ console.push("debug", ...arguments);
|
||||
\\};
|
||||
;
|
||||
res = try evalJS(js_env, alloc, init, "init");
|
||||
if (!res.success) {
|
||||
return res;
|
||||
}
|
||||
var res = try evalJS(js_env, alloc, init, "init");
|
||||
if (!res.ok) return res;
|
||||
res.deinit(alloc);
|
||||
|
||||
// loop hover the scripts.
|
||||
@@ -122,20 +118,14 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const
|
||||
}
|
||||
|
||||
res = try evalJS(js_env, alloc, try loader.get(path), src);
|
||||
if (!res.success) {
|
||||
return res;
|
||||
}
|
||||
if (!res.ok) return res;
|
||||
res.deinit(alloc);
|
||||
}
|
||||
|
||||
// If the script as a source text, execute it.
|
||||
const src = try parser.nodeTextContent(s) orelse continue;
|
||||
res = try evalJS(js_env, alloc, src, "");
|
||||
|
||||
// return the first failure.
|
||||
if (!res.success) {
|
||||
return res;
|
||||
}
|
||||
if (!res.ok) return res;
|
||||
res.deinit(alloc);
|
||||
}
|
||||
|
||||
@@ -150,25 +140,52 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const
|
||||
);
|
||||
|
||||
// wait for all async executions
|
||||
res = try js_env.waitTryCatch(alloc);
|
||||
if (!res.success) {
|
||||
return res;
|
||||
}
|
||||
res.deinit(alloc);
|
||||
var try_catch: jsruntime.TryCatch = undefined;
|
||||
try_catch.init(js_env);
|
||||
defer try_catch.deinit();
|
||||
js_env.wait() catch {
|
||||
return .{
|
||||
.ok = false,
|
||||
.msg = try try_catch.err(alloc, js_env),
|
||||
};
|
||||
};
|
||||
|
||||
// Check the final test status.
|
||||
res = try evalJS(js_env, alloc, "report.status;", "teststatus");
|
||||
if (!res.success) {
|
||||
return res;
|
||||
}
|
||||
if (!res.ok) return res;
|
||||
res.deinit(alloc);
|
||||
|
||||
// return the detailed result.
|
||||
return try evalJS(js_env, alloc, "report.log", "teststatus");
|
||||
}
|
||||
|
||||
fn evalJS(env: jsruntime.Env, alloc: std.mem.Allocator, script: []const u8, name: ?[]const u8) !jsruntime.JSResult {
|
||||
return try env.execTryCatch(alloc, script, name);
|
||||
pub const Res = struct {
|
||||
ok: bool,
|
||||
msg: ?[]const u8,
|
||||
|
||||
pub fn deinit(res: Res, alloc: std.mem.Allocator) void {
|
||||
if (res.msg) |msg| {
|
||||
alloc.free(msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn evalJS(env: jsruntime.Env, alloc: std.mem.Allocator, script: []const u8, name: ?[]const u8) !Res {
|
||||
var try_catch: jsruntime.TryCatch = undefined;
|
||||
try_catch.init(env);
|
||||
defer try_catch.deinit();
|
||||
|
||||
const v = env.exec(script, name) catch {
|
||||
return .{
|
||||
.ok = false,
|
||||
.msg = try try_catch.err(alloc, env),
|
||||
};
|
||||
};
|
||||
|
||||
return .{
|
||||
.ok = true,
|
||||
.msg = try v.toString(alloc, env),
|
||||
};
|
||||
}
|
||||
|
||||
// browse the path to find the tests list.
|
||||
|
||||
@@ -67,28 +67,22 @@ pub const Suite = struct {
|
||||
pass: bool,
|
||||
name: []const u8,
|
||||
message: ?[]const u8,
|
||||
stack: ?[]const u8,
|
||||
cases: ?[]Case,
|
||||
|
||||
// caller owns the wpt.Suite.
|
||||
// owner must call deinit().
|
||||
pub fn init(alloc: std.mem.Allocator, name: []const u8, pass: bool, res: []const u8, stack: ?[]const u8) !Suite {
|
||||
pub fn init(alloc: std.mem.Allocator, name: []const u8, pass: bool, res: []const u8) !Suite {
|
||||
var suite = Suite{
|
||||
.alloc = alloc,
|
||||
.pass = false,
|
||||
.name = try alloc.dupe(u8, name),
|
||||
.message = null,
|
||||
.stack = null,
|
||||
.cases = null,
|
||||
};
|
||||
|
||||
// handle JS error.
|
||||
if (!pass) {
|
||||
suite.message = try alloc.dupe(u8, res);
|
||||
if (stack) |st| {
|
||||
suite.stack = try alloc.dupe(u8, st);
|
||||
}
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
||||
@@ -155,10 +149,6 @@ pub const Suite = struct {
|
||||
pub fn deinit(self: Suite) void {
|
||||
self.alloc.free(self.name);
|
||||
|
||||
if (self.stack) |stack| {
|
||||
self.alloc.free(stack);
|
||||
}
|
||||
|
||||
if (self.message) |res| {
|
||||
self.alloc.free(res);
|
||||
}
|
||||
@@ -175,9 +165,6 @@ pub const Suite = struct {
|
||||
if (self.message) |v| {
|
||||
return v;
|
||||
}
|
||||
if (self.stack) |v| {
|
||||
return v;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
};
|
||||
@@ -199,7 +186,7 @@ test "success test case" {
|
||||
,
|
||||
};
|
||||
|
||||
const suite = Suite.init(alloc, "foo", res.pass, res.result, null) catch unreachable; // TODO
|
||||
const suite = Suite.init(alloc, "foo", res.pass, res.result) catch unreachable; // TODO
|
||||
defer suite.deinit();
|
||||
|
||||
try testing.expect(suite.pass == true);
|
||||
@@ -226,7 +213,7 @@ test "failed test case" {
|
||||
,
|
||||
};
|
||||
|
||||
const suite = Suite.init(alloc, "foo", res.pass, res.result, null) catch unreachable; // TODO
|
||||
const suite = Suite.init(alloc, "foo", res.pass, res.result) catch unreachable; // TODO
|
||||
defer suite.deinit();
|
||||
|
||||
try testing.expect(suite.pass == false);
|
||||
@@ -251,7 +238,7 @@ test "invalid result" {
|
||||
,
|
||||
};
|
||||
|
||||
const suite = Suite.init(alloc, "foo", res.pass, res.result, null) catch unreachable; // TODO
|
||||
const suite = Suite.init(alloc, "foo", res.pass, res.result) catch unreachable; // TODO
|
||||
defer suite.deinit();
|
||||
|
||||
try testing.expect(suite.pass == false);
|
||||
@@ -266,7 +253,7 @@ test "invalid result" {
|
||||
,
|
||||
};
|
||||
|
||||
const suite2 = Suite.init(alloc, "foo", res2.pass, res2.result, null) catch unreachable; // TODO
|
||||
const suite2 = Suite.init(alloc, "foo", res2.pass, res2.result) catch unreachable; // TODO
|
||||
defer suite2.deinit();
|
||||
|
||||
try testing.expect(suite2.pass == false);
|
||||
|
||||
1
vendor/tls.zig
vendored
Submodule
1
vendor/tls.zig
vendored
Submodule
Submodule vendor/tls.zig added at 0ea9e6d769
2
vendor/zig-js-runtime
vendored
2
vendor/zig-js-runtime
vendored
Submodule vendor/zig-js-runtime updated: a820cf4387...f2a6e94a18
Reference in New Issue
Block a user