From 7835f133fedfc40522ed7867838ec6f4921f36de Mon Sep 17 00:00:00 2001
From: Rohit <71192000+rohitsux@users.noreply.github.com>
Date: Fri, 12 Jun 2026 11:04:21 +0530
Subject: [PATCH 1/2] feat(webapi): reflect attributes on HTMLSourceElement
Add the reflected IDL attributes to the existing HTMLSourceElement,
which previously reflected none: src (resolved as a URL like
HTMLEmbedElement.src), srcset, sizes, media, type, width and height.
Mirrors the reflection idiom used by HTMLEmbedElement. width and height
are reflected as strings, matching the sibling HTMLEmbedElement
accessors. Adds element/html/source.html covering parse-from-markup,
property/attribute round-trips and src URL resolution.
---
src/browser/tests/element/html/source.html | 77 +++++++++++++++++++++
src/browser/webapi/element/html/Source.zig | 78 ++++++++++++++++++++++
2 files changed, 155 insertions(+)
create mode 100644 src/browser/tests/element/html/source.html
diff --git a/src/browser/tests/element/html/source.html b/src/browser/tests/element/html/source.html
new file mode 100644
index 000000000..29c93c343
--- /dev/null
+++ b/src/browser/tests/element/html/source.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/element/html/Source.zig b/src/browser/webapi/element/html/Source.zig
index 8bb43fb02..f654959d7 100644
--- a/src/browser/webapi/element/html/Source.zig
+++ b/src/browser/webapi/element/html/Source.zig
@@ -1,4 +1,5 @@
const js = @import("../../../js/js.zig");
+const Frame = @import("../../../Frame.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
@@ -10,10 +11,74 @@ _proto: *HtmlElement,
pub fn asElement(self: *Source) *Element {
return self._proto._proto;
}
+pub fn asConstElement(self: *const Source) *const Element {
+ return self._proto._proto;
+}
pub fn asNode(self: *Source) *Node {
return self.asElement().asNode();
}
+pub fn getSrc(self: *const Source, frame: *Frame) ![]const u8 {
+ const element = self.asConstElement();
+ const src = element.getAttributeSafe(comptime .wrap("src")) orelse return "";
+ if (src.len == 0) {
+ return "";
+ }
+ return element.asConstNode().resolveURL(src, frame, .{});
+}
+
+pub fn setSrc(self: *Source, value: []const u8, frame: *Frame) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("src"), .wrap(value), frame);
+}
+
+pub fn getSrcset(self: *const Source) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("srcset")) orelse "";
+}
+
+pub fn setSrcset(self: *Source, value: []const u8, frame: *Frame) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("srcset"), .wrap(value), frame);
+}
+
+pub fn getSizes(self: *const Source) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("sizes")) orelse "";
+}
+
+pub fn setSizes(self: *Source, value: []const u8, frame: *Frame) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("sizes"), .wrap(value), frame);
+}
+
+pub fn getMedia(self: *const Source) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("media")) orelse "";
+}
+
+pub fn setMedia(self: *Source, value: []const u8, frame: *Frame) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("media"), .wrap(value), frame);
+}
+
+pub fn getType(self: *const Source) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("type")) orelse "";
+}
+
+pub fn setType(self: *Source, value: []const u8, frame: *Frame) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("type"), .wrap(value), frame);
+}
+
+pub fn getWidth(self: *const Source) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("width")) orelse "";
+}
+
+pub fn setWidth(self: *Source, value: []const u8, frame: *Frame) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("width"), .wrap(value), frame);
+}
+
+pub fn getHeight(self: *const Source) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("height")) orelse "";
+}
+
+pub fn setHeight(self: *Source, value: []const u8, frame: *Frame) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("height"), .wrap(value), frame);
+}
+
pub const JsApi = struct {
pub const bridge = js.Bridge(Source);
@@ -22,4 +87,17 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
+
+ pub const height = bridge.accessor(Source.getHeight, Source.setHeight, .{ .ce_reactions = true });
+ pub const media = bridge.accessor(Source.getMedia, Source.setMedia, .{ .ce_reactions = true });
+ pub const sizes = bridge.accessor(Source.getSizes, Source.setSizes, .{ .ce_reactions = true });
+ pub const src = bridge.accessor(Source.getSrc, Source.setSrc, .{ .ce_reactions = true });
+ pub const srcset = bridge.accessor(Source.getSrcset, Source.setSrcset, .{ .ce_reactions = true });
+ pub const @"type" = bridge.accessor(Source.getType, Source.setType, .{ .ce_reactions = true });
+ pub const width = bridge.accessor(Source.getWidth, Source.setWidth, .{ .ce_reactions = true });
};
+
+const testing = @import("../../../../testing.zig");
+test "WebApi: HTML.Source" {
+ try testing.htmlRunner("element/html/source.html", .{});
+}
From 67f947f606fbe4e9a0e210d271e3785269b49e52 Mon Sep 17 00:00:00 2001
From: Karl Seguin
Date: Fri, 12 Jun 2026 18:29:46 +0800
Subject: [PATCH 2/2] Reflect source width/height as unsigned long per spec
Co-Authored-By: Claude Fable 5
---
src/browser/tests/element/html/source.html | 18 ++++++++++--------
src/browser/webapi/element/html/Source.zig | 21 +++++++++++++--------
2 files changed, 23 insertions(+), 16 deletions(-)
diff --git a/src/browser/tests/element/html/source.html b/src/browser/tests/element/html/source.html
index 29c93c343..461cb55ab 100644
--- a/src/browser/tests/element/html/source.html
+++ b/src/browser/tests/element/html/source.html
@@ -13,8 +13,8 @@
testing.expectEqual('100vw', s1.sizes);
testing.expectEqual('(min-width: 600px)', s1.media);
testing.expectEqual('video/webm', s1.type);
- testing.expectEqual('640', s1.width);
- testing.expectEqual('480', s1.height);
+ testing.expectEqual(640, s1.width);
+ testing.expectEqual(480, s1.height);
const s2 = document.getElementById('s2');
testing.expectEqual('', s2.src);
@@ -22,8 +22,8 @@
testing.expectEqual('', s2.sizes);
testing.expectEqual('', s2.media);
testing.expectEqual('', s2.type);
- testing.expectEqual('', s2.width);
- testing.expectEqual('', s2.height);
+ testing.expectEqual(0, s2.width);
+ testing.expectEqual(0, s2.height);
}
@@ -51,15 +51,17 @@
s.setAttribute('type', 'image/svg+xml');
testing.expectEqual('image/svg+xml', s.type);
- s.width = '320';
+ s.width = 320;
testing.expectEqual('320', s.getAttribute('width'));
s.setAttribute('width', '160');
- testing.expectEqual('160', s.width);
+ testing.expectEqual(160, s.width);
+ s.setAttribute('width', 'not-a-number');
+ testing.expectEqual(0, s.width);
- s.height = '240';
+ s.height = 240;
testing.expectEqual('240', s.getAttribute('height'));
s.setAttribute('height', '120');
- testing.expectEqual('120', s.height);
+ testing.expectEqual(120, s.height);
}
diff --git a/src/browser/webapi/element/html/Source.zig b/src/browser/webapi/element/html/Source.zig
index f654959d7..24c24042a 100644
--- a/src/browser/webapi/element/html/Source.zig
+++ b/src/browser/webapi/element/html/Source.zig
@@ -1,3 +1,4 @@
+const std = @import("std");
const js = @import("../../../js/js.zig");
const Frame = @import("../../../Frame.zig");
const Node = @import("../../Node.zig");
@@ -63,20 +64,24 @@ pub fn setType(self: *Source, value: []const u8, frame: *Frame) !void {
try self.asElement().setAttributeSafe(comptime .wrap("type"), .wrap(value), frame);
}
-pub fn getWidth(self: *const Source) []const u8 {
- return self.asConstElement().getAttributeSafe(comptime .wrap("width")) orelse "";
+pub fn getWidth(self: *const Source) u32 {
+ const attr = self.asConstElement().getAttributeSafe(comptime .wrap("width")) orelse return 0;
+ return std.fmt.parseUnsigned(u32, attr, 10) catch 0;
}
-pub fn setWidth(self: *Source, value: []const u8, frame: *Frame) !void {
- try self.asElement().setAttributeSafe(comptime .wrap("width"), .wrap(value), frame);
+pub fn setWidth(self: *Source, value: u32, frame: *Frame) !void {
+ const str = try std.fmt.allocPrint(frame.call_arena, "{d}", .{value});
+ try self.asElement().setAttributeSafe(comptime .wrap("width"), .wrap(str), frame);
}
-pub fn getHeight(self: *const Source) []const u8 {
- return self.asConstElement().getAttributeSafe(comptime .wrap("height")) orelse "";
+pub fn getHeight(self: *const Source) u32 {
+ const attr = self.asConstElement().getAttributeSafe(comptime .wrap("height")) orelse return 0;
+ return std.fmt.parseUnsigned(u32, attr, 10) catch 0;
}
-pub fn setHeight(self: *Source, value: []const u8, frame: *Frame) !void {
- try self.asElement().setAttributeSafe(comptime .wrap("height"), .wrap(value), frame);
+pub fn setHeight(self: *Source, value: u32, frame: *Frame) !void {
+ const str = try std.fmt.allocPrint(frame.call_arena, "{d}", .{value});
+ try self.asElement().setAttributeSafe(comptime .wrap("height"), .wrap(str), frame);
}
pub const JsApi = struct {