diff --git a/src/browser/Factory.zig b/src/browser/Factory.zig index c5ede22b..582291a3 100644 --- a/src/browser/Factory.zig +++ b/src/browser/Factory.zig @@ -247,16 +247,15 @@ fn eventInit(arena: Allocator, typ: String, value: anytype) !Event { }; } -pub fn blob(self: *Factory, child: anytype) !*@TypeOf(child) { - const allocator = self._slab.allocator(); - +pub fn blob(_: *const Factory, arena: Allocator, child: anytype) !*@TypeOf(child) { // Special case: Blob has slice and mime fields, so we need manual setup const chain = try PrototypeChain( &.{ Blob, @TypeOf(child) }, - ).allocate(allocator); + ).allocate(arena); const blob_ptr = chain.get(0); blob_ptr.* = .{ + ._arena = arena, ._type = unionInit(Blob.Type, chain.get(1)), ._slice = "", ._mime = "", diff --git a/src/browser/webapi/Blob.zig b/src/browser/webapi/Blob.zig index aa955ce5..5b8bf81c 100644 --- a/src/browser/webapi/Blob.zig +++ b/src/browser/webapi/Blob.zig @@ -21,8 +21,12 @@ const Writer = std.Io.Writer; const js = @import("../js/js.zig"); const Page = @import("../Page.zig"); +const Session = @import("../Session.zig"); + const Mime = @import("../Mime.zig"); +const Allocator = std.mem.Allocator; + /// https://w3c.github.io/FileAPI/#blob-section /// https://developer.mozilla.org/en-US/docs/Web/API/Blob const Blob = @This(); @@ -31,6 +35,8 @@ pub const _prototype_root = true; _type: Type, +_arena: Allocator, + /// Immutable slice of blob. /// Note that another blob may hold a pointer/slice to this, /// so its better to leave the deallocation of it to arena allocator. @@ -69,6 +75,9 @@ pub fn initWithMimeValidation( validate_mime: bool, page: *Page, ) !*Blob { + const arena = try page.getArena(.{ .debug = "Blob" }); + errdefer page.releaseArena(arena); + const options: InitOptions = maybe_options orelse .{}; const mime: []const u8 = blk: { @@ -77,7 +86,7 @@ pub fn initWithMimeValidation( break :blk ""; } - const buf = try page.arena.dupe(u8, t); + const buf = try arena.dupe(u8, t); if (validate_mime) { // Full MIME parsing per MIME sniff spec (for Content-Type headers) @@ -99,7 +108,7 @@ pub fn initWithMimeValidation( const data = blk: { if (maybe_blob_parts) |blob_parts| { - var w: Writer.Allocating = .init(page.arena); + var w: Writer.Allocating = .init(arena); const use_native_endings = std.mem.eql(u8, options.endings, "native"); try writeBlobParts(&w.writer, blob_parts, use_native_endings); @@ -109,11 +118,19 @@ pub fn initWithMimeValidation( break :blk ""; }; - return page._factory.create(Blob{ + const self = try arena.create(Blob); + self.* = .{ + ._arena = arena, ._type = .generic, ._slice = data, ._mime = mime, - }); + }; + return self; +} + +pub fn deinit(self: *Blob, shutdown: bool, session: *Session) void { + _ = shutdown; + session.releaseArena(self._arena); } const largest_vector = @max(std.simd.suggestVectorLength(u8) orelse 1, 8); @@ -264,57 +281,31 @@ pub fn bytes(self: *const Blob, page: *Page) !js.Promise { /// from a subset of the blob on which it's called. pub fn slice( self: *const Blob, - maybe_start: ?i32, - maybe_end: ?i32, - maybe_content_type: ?[]const u8, + start_: ?i32, + end_: ?i32, + content_type_: ?[]const u8, page: *Page, ) !*Blob { - const mime: []const u8 = blk: { - if (maybe_content_type) |content_type| { - if (content_type.len == 0) { - break :blk ""; - } + const data = self._slice; - break :blk try page.dupeString(content_type); + const start = blk: { + const requested_start = start_ orelse break :blk 0; + if (requested_start < 0) { + break :blk data.len -| @abs(requested_start); } - - break :blk ""; + break :blk @min(data.len, @as(u31, @intCast(requested_start))); }; - const data = self._slice; - if (maybe_start) |_start| { - const start = blk: { - if (_start < 0) { - break :blk data.len -| @abs(_start); - } + const end: usize = blk: { + const requested_end = end_ orelse break :blk data.len; + if (requested_end < 0) { + break :blk @max(start, data.len -| @abs(requested_end)); + } - break :blk @min(data.len, @as(u31, @intCast(_start))); - }; + break :blk @min(data.len, @max(start, @as(u31, @intCast(requested_end)))); + }; - const end: usize = blk: { - if (maybe_end) |_end| { - if (_end < 0) { - break :blk @max(start, data.len -| @abs(_end)); - } - - break :blk @min(data.len, @max(start, @as(u31, @intCast(_end)))); - } - - break :blk data.len; - }; - - return page._factory.create(Blob{ - ._type = .generic, - ._slice = data[start..end], - ._mime = mime, - }); - } - - return page._factory.create(Blob{ - ._type = .generic, - ._slice = data, - ._mime = mime, - }); + return Blob.init(&.{data[start..end]}, .{ .type = content_type_ orelse "" }, page); } /// Returns the size of the Blob in bytes. @@ -334,6 +325,8 @@ pub const JsApi = struct { pub const name = "Blob"; pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; + pub const weak = true; + pub const finalizer = bridge.finalizer(Blob.deinit); }; pub const constructor = bridge.constructor(Blob.init, .{}); diff --git a/src/browser/webapi/File.zig b/src/browser/webapi/File.zig index a67a8a6f..f41e44bb 100644 --- a/src/browser/webapi/File.zig +++ b/src/browser/webapi/File.zig @@ -18,9 +18,11 @@ const std = @import("std"); -const Page = @import("../Page.zig"); -const Blob = @import("Blob.zig"); const js = @import("../js/js.zig"); +const Page = @import("../Page.zig"); +const Session = @import("../Session.zig"); + +const Blob = @import("Blob.zig"); const File = @This(); @@ -29,7 +31,13 @@ _proto: *Blob, // TODO: Implement File API. pub fn init(page: *Page) !*File { - return page._factory.blob(File{ ._proto = undefined }); + const arena = try page.getArena(.{ .debug = "File" }); + errdefer page.releaseArena(arena); + return page._factory.blob(arena, File{ ._proto = undefined }); +} + +pub fn deinit(self: *File, shutdown: bool, session: *Session) void { + self._proto.deinit(shutdown, session); } pub const JsApi = struct { @@ -39,6 +47,8 @@ pub const JsApi = struct { pub const name = "File"; pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; + pub const weak = true; + pub const finalizer = bridge.finalizer(File.deinit); }; pub const constructor = bridge.constructor(File.init, .{});