diff --git a/src/browser/Page.zig b/src/browser/Page.zig index f8323f97..4b4c99e5 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -39,6 +39,7 @@ const ScriptManager = @import("ScriptManager.zig"); const Parser = @import("parser/Parser.zig"); const URL = @import("URL.zig"); +const Blob = @import("webapi/Blob.zig"); const Node = @import("webapi/Node.zig"); const Event = @import("webapi/Event.zig"); const EventTarget = @import("webapi/EventTarget.zig"); @@ -133,6 +134,9 @@ _element_namespace_uris: Element.NamespaceUriLookup = .empty, /// ``` _element_attr_listeners: GlobalEventHandlersLookup = .empty, +// Blob URL registry for URL.createObjectURL/revokeObjectURL +_blob_urls: std.StringHashMapUnmanaged(*Blob) = .{}, + /// `load` events that'll be fired before window's `load` event. /// A call to `documentIsComplete` (which calls `_documentIsComplete`) resets it. _to_load: std.ArrayList(*Element) = .{}, diff --git a/src/browser/tests/url.html b/src/browser/tests/url.html index 80b70823..c5a71a85 100644 --- a/src/browser/tests/url.html +++ b/src/browser/tests/url.html @@ -656,3 +656,45 @@ testing.expectEqual("?a=%7E&b=%7E&c=foo", url.search); } + + diff --git a/src/browser/webapi/URL.zig b/src/browser/webapi/URL.zig index e886c8eb..0f2dc58b 100644 --- a/src/browser/webapi/URL.zig +++ b/src/browser/webapi/URL.zig @@ -22,6 +22,7 @@ const js = @import("../js/js.zig"); const U = @import("../URL.zig"); const Page = @import("../Page.zig"); const URLSearchParams = @import("net/URLSearchParams.zig"); +const Blob = @import("Blob.zig"); const Allocator = std.mem.Allocator; @@ -228,6 +229,30 @@ pub fn canParse(url: []const u8, base_: ?[]const u8) bool { return U.isCompleteHTTPUrl(url); } +pub fn createObjectURL(blob: *Blob, page: *Page) ![]const u8 { + var uuid_buf: [36]u8 = undefined; + @import("../../id.zig").uuidv4(&uuid_buf); + + const origin = (try page.getOrigin(page.call_arena)) orelse "null"; + const blob_url = try std.fmt.allocPrint( + page.arena, + "blob:{s}/{s}", + .{ origin, uuid_buf }, + ); + try page._blob_urls.put(page.arena, blob_url, blob); + return blob_url; +} + +pub fn revokeObjectURL(url: []const u8, page: *Page) void { + // Per spec: silently ignore non-blob URLs + if (!std.mem.startsWith(u8, url, "blob:")) { + return; + } + + // Remove from registry (no-op if not found) + _ = page._blob_urls.remove(url); +} + pub const JsApi = struct { pub const bridge = js.Bridge(URL); @@ -239,6 +264,8 @@ pub const JsApi = struct { pub const constructor = bridge.constructor(URL.init, .{}); pub const canParse = bridge.function(URL.canParse, .{ .static = true }); + pub const createObjectURL = bridge.function(URL.createObjectURL, .{ .static = true }); + pub const revokeObjectURL = bridge.function(URL.revokeObjectURL, .{ .static = true }); pub const toString = bridge.function(URL.toString, .{}); pub const toJSON = bridge.function(URL.toString, .{}); pub const href = bridge.accessor(URL.toString, URL.setHref, .{});