diff --git a/src/browser/ScriptManagerBase.zig b/src/browser/ScriptManagerBase.zig index 3dd2cdd4..b73121a6 100644 --- a/src/browser/ScriptManagerBase.zig +++ b/src/browser/ScriptManagerBase.zig @@ -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 diff --git a/src/browser/tests/net/xhr.html b/src/browser/tests/net/xhr.html index e03b885b..62780f97 100644 --- a/src/browser/tests/net/xhr.html +++ b/src/browser/tests/net/xhr.html @@ -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); }); } @@ -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); }); } diff --git a/src/browser/tests/testing.js b/src/browser/tests/testing.js index 79a61070..ca44d8ec 100644 --- a/src/browser/tests/testing.js +++ b/src/browser/tests/testing.js @@ -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; } }; diff --git a/src/browser/tests/worker/import-script1.js b/src/browser/tests/worker/import-script1.js new file mode 100644 index 00000000..768b69f0 --- /dev/null +++ b/src/browser/tests/worker/import-script1.js @@ -0,0 +1 @@ +postMessage('importScripts-1'); diff --git a/src/browser/tests/worker/import-script2.js b/src/browser/tests/worker/import-script2.js new file mode 100644 index 00000000..a6af6ac6 --- /dev/null +++ b/src/browser/tests/worker/import-script2.js @@ -0,0 +1 @@ +postMessage('importScripts-2'); diff --git a/src/browser/tests/worker/importScripts-worker.js b/src/browser/tests/worker/importScripts-worker.js new file mode 100644 index 00000000..a64177fd --- /dev/null +++ b/src/browser/tests/worker/importScripts-worker.js @@ -0,0 +1 @@ +importScripts('import-script1.js', 'import-script2.js'); diff --git a/src/browser/tests/worker/worker.html b/src/browser/tests/worker/worker.html index b6d671b8..dda6d1a8 100644 --- a/src/browser/tests/worker/worker.html +++ b/src/browser/tests/worker/worker.html @@ -275,3 +275,23 @@ }); } + + diff --git a/src/browser/webapi/WorkerGlobalScope.zig b/src/browser/webapi/WorkerGlobalScope.zig index 67d30d8e..95f1f303 100644 --- a/src/browser/webapi/WorkerGlobalScope.zig +++ b/src/browser/webapi/WorkerGlobalScope.zig @@ -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, .{});