Worker.importScripts

I also noticed that failures in async assertions could be ignored, so I fixed
that and fixed a couple failing XHR tests.
This commit is contained in:
Karl Seguin
2026-05-04 13:50:09 +08:00
parent 0c55875b63
commit 77a1fdc2a0
8 changed files with 104 additions and 20 deletions

View File

@@ -648,12 +648,19 @@ pub const Script = struct {
pub fn errorCallback(ctx: *anyopaque, err: anyerror) void {
const self: *Script = @ptrCast(@alignCast(ctx));
log.warn(.http, "script fetch error", .{
.err = err,
.req = self.url,
.extra = std.meta.activeTag(self.extra),
.status = self.status,
});
if (self.status == 404) {
log.info(.http, "script 404", .{
.req = self.url,
.extra = std.meta.activeTag(self.extra),
});
} else {
log.warn(.http, "script fetch error", .{
.err = err,
.req = self.url,
.extra = std.meta.activeTag(self.extra),
.status = self.status,
});
}
if (self.extra == .frame and self.extra.frame.mode == .normal) {
// This is blocked in a loop at the end of addFromElement, setting

View File

@@ -76,10 +76,7 @@
await state.done(() => {
testing.expectEqual(200, req3.status);
testing.expectEqual('OK', req3.statusText);
testing.expectEqual('9000!!!', req3.response.over);
testing.expectEqual("number", typeof json.updated_at);
testing.expectEqual(1765867200000, json.updated_at);
testing.expectEqual({over: '9000!!!',updated_at:1765867200000}, json);
testing.expectEqual({over: '9000!!!', updated_at:1765867200000}, req3.response);
});
}
</script>
@@ -142,8 +139,7 @@
testing.expectEqual(200, req6.status);
testing.expectEqual('OK', req6.statusText);
testing.expectEqual(7, req6.response.byteLength);
testing.expectEqual([0, 0, 1, 2, 0, 0, 9], new Int32Array(req6.response));
testing.expectEqual('', typeof req6.response);
testing.expectEqual([0, 0, 1, 2, 0, 0, 9], new Int8Array(req6.response));
testing.expectEqual('arraybuffer', req6.responseType);
});
}

View File

@@ -87,7 +87,12 @@
const res = await this.promise;
async_pending.delete(script_id);
async_capture = this.capture;
cb(res);
try {
cb(res);
} catch (err) {
console.warn(script_id, err);
failed = true;
}
async_capture = false;
}
};

View File

@@ -0,0 +1 @@
postMessage('importScripts-1');

View File

@@ -0,0 +1 @@
postMessage('importScripts-2');

View File

@@ -0,0 +1 @@
importScripts('import-script1.js', 'import-script2.js');

View File

@@ -275,3 +275,23 @@
});
}
</script>
<script id="importScripts" type=module>
{
const state = await testing.async();
const worker = new Worker('importScripts-worker.js');
var received = [];
worker.onmessage = function(event) {
received.push(event);
if (received.length == 2) {
state.resolve();
}
};
await state.done(() => {
testing.expectEqual('importScripts-1', received[0].data);
testing.expectEqual('importScripts-2', received[1].data);
});
}
</script>

View File

@@ -33,6 +33,7 @@ const EventManagerBase = @import("../EventManagerBase.zig");
const ScriptManagerBase = @import("../ScriptManagerBase.zig");
const Blob = @import("Blob.zig");
const Event = @import("Event.zig");
const Worker = @import("Worker.zig");
const Crypto = @import("Crypto.zig");
const Console = @import("Console.zig");
@@ -160,8 +161,6 @@ pub fn asEventTarget(self: *WorkerGlobalScope) *EventTarget {
return self._proto;
}
const Event = @import("Event.zig");
// Dispatch an event to listeners on the given target within this worker context.
pub fn dispatch(
self: *WorkerGlobalScope,
@@ -343,6 +342,64 @@ pub fn close(self: *WorkerGlobalScope) void {
self._closed = true;
}
pub fn importScripts(self: *WorkerGlobalScope, urls: []const [:0]const u8) !void {
const session = self._session;
const arena = try session.getArena(.large, "importScript");
defer session.releaseArena(arena);
for (urls) |url| {
defer session.arena_pool.resetRetain(arena);
try self.importScript(arena, url);
}
}
fn importScript(self: *WorkerGlobalScope, arena: Allocator, url: [:0]const u8) !void {
const session = self._session;
const resolved_url = try URL.resolve(arena, self.url, url, .{});
const http_client = session.browser.http_client;
var headers = try http_client.newHeaders();
try self.headersForRequest(&headers);
const response = http_client.syncRequest(arena, .{
.url = resolved_url,
.method = .GET,
.frame_id = self._frame_id,
.loader_id = self._loader_id,
.headers = headers,
.cookie_jar = &session.cookie_jar,
.cookie_origin = self.url,
.resource_type = .script,
.notification = session.notification,
}) catch |err| {
log.warn(.http, "importScript", .{ .url = resolved_url, .err = err });
return error.NetworkError;
};
if (response.status != 200) {
log.warn(.http, "importScript", .{ .url = resolved_url, .status = response.status });
return error.NetworkError;
}
var ls: JS.Local.Scope = undefined;
self.js.localScope(&ls);
defer ls.deinit();
var try_catch: JS.TryCatch = undefined;
try_catch.init(&ls.local);
defer try_catch.deinit();
_ = ls.local.eval(response.body.items, url) catch |err| {
const caught = try_catch.caughtOrError(arena, err);
log.err(.browser, "importScript", .{ .url = resolved_url, .caught = caught });
return;
};
ls.local.runMacrotasks();
}
pub fn reportError(self: *WorkerGlobalScope, err: JS.Value) !void {
const error_event = try ErrorEvent.initTrusted(comptime .wrap("error"), .{
.@"error" = try err.temp(),
@@ -397,11 +454,6 @@ pub fn fetch(_: *const WorkerGlobalScope, input: Fetch.Input, options: ?Fetch.In
return Fetch.init(input, options, exec);
}
// TODO: importScripts - needs script loading infrastructure
// TODO: location - needs WorkerLocation
// TODO: navigator - needs WorkerNavigator
// TODO: Timer functions - need scheduler integration
const FunctionSetter = union(enum) {
func: JS.Function.Global,
anything: JS.Value,
@@ -493,6 +545,7 @@ pub const JsApi = struct {
pub const reportError = bridge.function(WorkerGlobalScope.reportError, .{});
pub const close = bridge.function(WorkerGlobalScope.close, .{});
pub const fetch = bridge.function(WorkerGlobalScope.fetch, .{});
pub const importScripts = bridge.function(WorkerGlobalScope.importScripts, .{ .dom_exception = true });
pub const onmessage = bridge.accessor(WorkerGlobalScope.getOnMessage, WorkerGlobalScope.setOnMessage, .{});
pub const onmessageerror = bridge.accessor(WorkerGlobalScope.getOnMessageError, WorkerGlobalScope.setOnMessageError, .{});