mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Add URL.createObjectURL and URL.revokeObjectURL
This commit is contained in:
@@ -39,6 +39,7 @@ const ScriptManager = @import("ScriptManager.zig");
|
|||||||
const Parser = @import("parser/Parser.zig");
|
const Parser = @import("parser/Parser.zig");
|
||||||
|
|
||||||
const URL = @import("URL.zig");
|
const URL = @import("URL.zig");
|
||||||
|
const Blob = @import("webapi/Blob.zig");
|
||||||
const Node = @import("webapi/Node.zig");
|
const Node = @import("webapi/Node.zig");
|
||||||
const Event = @import("webapi/Event.zig");
|
const Event = @import("webapi/Event.zig");
|
||||||
const EventTarget = @import("webapi/EventTarget.zig");
|
const EventTarget = @import("webapi/EventTarget.zig");
|
||||||
@@ -132,6 +133,9 @@ _element_scroll_positions: Element.ScrollPositionLookup = .empty,
|
|||||||
/// ```
|
/// ```
|
||||||
_element_attr_listeners: GlobalEventHandlersLookup = .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.
|
/// `load` events that'll be fired before window's `load` event.
|
||||||
/// A call to `documentIsComplete` (which calls `_documentIsComplete`) resets it.
|
/// A call to `documentIsComplete` (which calls `_documentIsComplete`) resets it.
|
||||||
_to_load: std.ArrayList(*Element) = .{},
|
_to_load: std.ArrayList(*Element) = .{},
|
||||||
|
|||||||
@@ -656,3 +656,45 @@
|
|||||||
testing.expectEqual("?a=%7E&b=%7E&c=foo", url.search);
|
testing.expectEqual("?a=%7E&b=%7E&c=foo", url.search);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script id=objectURL>
|
||||||
|
{
|
||||||
|
// Test createObjectURL with Blob
|
||||||
|
const blob = new Blob(['<html><body>Hello</body></html>'], { type: 'text/html' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
testing.expectEqual('string', typeof url);
|
||||||
|
testing.expectEqual(true, url.startsWith('blob:'));
|
||||||
|
testing.expectEqual(true, url.includes('http'));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Test revokeObjectURL
|
||||||
|
const blob = new Blob(['test'], { type: 'text/plain' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
testing.expectEqual(true, url.startsWith('blob:'));
|
||||||
|
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
URL.revokeObjectURL(url); // Second revoke should be safe
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Test with non-blob URL (should be safe/silent)
|
||||||
|
URL.revokeObjectURL('http://example.com/notblob');
|
||||||
|
URL.revokeObjectURL('');
|
||||||
|
testing.expectEqual(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Test uniqueness
|
||||||
|
const blob1 = new Blob(['test1']);
|
||||||
|
const blob2 = new Blob(['test2']);
|
||||||
|
const url1 = URL.createObjectURL(blob1);
|
||||||
|
const url2 = URL.createObjectURL(blob2);
|
||||||
|
|
||||||
|
testing.expectEqual(false, url1 === url2);
|
||||||
|
testing.expectEqual(true, url1.startsWith('blob:'));
|
||||||
|
testing.expectEqual(true, url2.startsWith('blob:'));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const js = @import("../js/js.zig");
|
|||||||
const U = @import("../URL.zig");
|
const U = @import("../URL.zig");
|
||||||
const Page = @import("../Page.zig");
|
const Page = @import("../Page.zig");
|
||||||
const URLSearchParams = @import("net/URLSearchParams.zig");
|
const URLSearchParams = @import("net/URLSearchParams.zig");
|
||||||
|
const Blob = @import("Blob.zig");
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
@@ -228,6 +229,30 @@ pub fn canParse(url: []const u8, base_: ?[]const u8) bool {
|
|||||||
return U.isCompleteHTTPUrl(url);
|
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 JsApi = struct {
|
||||||
pub const bridge = js.Bridge(URL);
|
pub const bridge = js.Bridge(URL);
|
||||||
|
|
||||||
@@ -239,6 +264,8 @@ pub const JsApi = struct {
|
|||||||
|
|
||||||
pub const constructor = bridge.constructor(URL.init, .{});
|
pub const constructor = bridge.constructor(URL.init, .{});
|
||||||
pub const canParse = bridge.function(URL.canParse, .{ .static = true });
|
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 toString = bridge.function(URL.toString, .{});
|
||||||
pub const toJSON = bridge.function(URL.toString, .{});
|
pub const toJSON = bridge.function(URL.toString, .{});
|
||||||
pub const href = bridge.accessor(URL.toString, URL.setHref, .{});
|
pub const href = bridge.accessor(URL.toString, URL.setHref, .{});
|
||||||
|
|||||||
Reference in New Issue
Block a user