diff --git a/src/browser/Frame.zig b/src/browser/Frame.zig index 3631ffb8f..36a610732 100644 --- a/src/browser/Frame.zig +++ b/src/browser/Frame.zig @@ -670,8 +670,11 @@ pub fn navigate(self: *Frame, request_url: [:0]const u8, opts: NavigateOpts) !vo }; const parse_arena = try self.getArena(.medium, "Frame.parseBlob"); defer self.releaseArena(parse_arena); + // A script executed mid-parse can revoke the blob URL, letting GC + // free the buffer under the parser; parse a copy. + const html = try parse_arena.dupe(u8, blob._slice); var parser = Parser.init(parse_arena, self.document.asNode(), self, .{ .allow_declarative_shadow = true }); - parser.parse(blob._slice); + parser.parse(html); } else { self.document.injectBlank(self) catch |err| { log.err(.browser, "inject blank", .{ .err = err }); diff --git a/src/browser/tests/worker/worker.html b/src/browser/tests/worker/worker.html index 39aaad81d..23201b9bd 100644 --- a/src/browser/tests/worker/worker.html +++ b/src/browser/tests/worker/worker.html @@ -483,3 +483,26 @@ }); } + + diff --git a/src/browser/webapi/FileReader.zig b/src/browser/webapi/FileReader.zig index 0c7896331..68e214b78 100644 --- a/src/browser/webapi/FileReader.zig +++ b/src/browser/webapi/FileReader.zig @@ -199,8 +199,10 @@ fn readInternal(self: *FileReader, blob: *Blob, read_type: ReadType) !void { return; } - // Perform the read (synchronous since data is in memory) - const data = blob._slice; + // Perform the read (synchronous since data is in memory). _result + // outlives this call and the blob can be GC'd before JS reads it, + // so the result must not borrow the blob's memory. + const data = try self._arena.dupe(u8, blob._slice); const size = data.len; try self.dispatch(.progress, .{ .loaded = size, .total = size }, exec); if (self._aborted) { diff --git a/src/network/HttpClient.zig b/src/network/HttpClient.zig index 867180c1d..2debff17d 100644 --- a/src/network/HttpClient.zig +++ b/src/network/HttpClient.zig @@ -2959,8 +2959,9 @@ const Synthetic = struct { const owner = transfer.owner orelse return error.BlobNotFound; const blob_urls = owner.blob_urls orelse return error.BlobNotFound; const blob = blob_urls.get(url) orelse return error.BlobNotFound; - content_type = blob._mime; - body = blob._slice; + // blob can be removed by the time we run, dupe it. + content_type = try arena.dupe(u8, blob._mime); + body = try arena.dupe(u8, blob._slice); } const has_content_type = content_type.len > 0;