add attribute reflections for 8 HTML element types

Wire up missing IDL properties for HTMLTimeElement (dateTime),
HTMLLIElement (value), HTMLOListElement (start, reversed, type),
HTMLOptGroupElement (disabled, label), HTMLQuoteElement (cite),
HTMLTableCellElement (colSpan, rowSpan), HTMLLabelElement (htmlFor),
and HTMLFieldSetElement (disabled, name).
This commit is contained in:
egrs
2026-02-18 11:36:58 +01:00
parent acc1f2f3d7
commit 2e64c461c3
16 changed files with 435 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<script src="../../testing.js"></script>
<fieldset id="fs1" disabled name="group1">
<input type="text">
</fieldset>
<fieldset id="fs2">
<input type="text">
</fieldset>
<script id="disabled">
{
const fs1 = document.getElementById('fs1');
testing.expectEqual(true, fs1.disabled);
fs1.disabled = false;
testing.expectEqual(false, fs1.disabled);
const fs2 = document.getElementById('fs2');
testing.expectEqual(false, fs2.disabled);
}
</script>
<script id="name">
{
const fs1 = document.getElementById('fs1');
testing.expectEqual('group1', fs1.name);
fs1.name = 'updated';
testing.expectEqual('updated', fs1.name);
const fs2 = document.getElementById('fs2');
testing.expectEqual('', fs2.name);
}
</script>

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<script src="../../testing.js"></script>
<label id="l1" for="input1">Name</label>
<input id="input1">
<script id="htmlFor">
{
const l1 = document.getElementById('l1');
testing.expectEqual('input1', l1.htmlFor);
l1.htmlFor = 'input2';
testing.expectEqual('input2', l1.htmlFor);
const l2 = document.createElement('label');
testing.expectEqual('', l2.htmlFor);
}
</script>

View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<script src="../../testing.js"></script>
<ol>
<li id="li1" value="5">Item</li>
<li id="li2">Item</li>
</ol>
<script id="value">
{
const li1 = document.getElementById('li1');
testing.expectEqual(5, li1.value);
li1.value = 10;
testing.expectEqual(10, li1.value);
const li2 = document.getElementById('li2');
testing.expectEqual(0, li2.value);
li2.value = -3;
testing.expectEqual(-3, li2.value);
}
</script>

View File

@@ -0,0 +1,51 @@
<!DOCTYPE html>
<script src="../../testing.js"></script>
<ol id="ol1" start="5" reversed type="a">
<li>Item</li>
</ol>
<ol id="ol2">
<li>Item</li>
</ol>
<script id="start">
{
const ol1 = document.getElementById('ol1');
testing.expectEqual(5, ol1.start);
ol1.start = 10;
testing.expectEqual(10, ol1.start);
const ol2 = document.getElementById('ol2');
testing.expectEqual(1, ol2.start);
}
</script>
<script id="reversed">
{
const ol1 = document.getElementById('ol1');
testing.expectEqual(true, ol1.reversed);
ol1.reversed = false;
testing.expectEqual(false, ol1.reversed);
const ol2 = document.getElementById('ol2');
testing.expectEqual(false, ol2.reversed);
ol2.reversed = true;
testing.expectEqual(true, ol2.reversed);
}
</script>
<script id="type">
{
const ol1 = document.getElementById('ol1');
testing.expectEqual('a', ol1.type);
ol1.type = '1';
testing.expectEqual('1', ol1.type);
const ol2 = document.getElementById('ol2');
testing.expectEqual('', ol2.type);
}
</script>

View File

@@ -0,0 +1,40 @@
<!DOCTYPE html>
<script src="../../testing.js"></script>
<select>
<optgroup id="og1" label="Group 1" disabled>
<option>A</option>
</optgroup>
<optgroup id="og2" label="Group 2">
<option>B</option>
</optgroup>
</select>
<script id="disabled">
{
const og1 = document.getElementById('og1');
testing.expectEqual(true, og1.disabled);
og1.disabled = false;
testing.expectEqual(false, og1.disabled);
const og2 = document.getElementById('og2');
testing.expectEqual(false, og2.disabled);
og2.disabled = true;
testing.expectEqual(true, og2.disabled);
}
</script>
<script id="label">
{
const og1 = document.getElementById('og1');
testing.expectEqual('Group 1', og1.label);
og1.label = 'Updated';
testing.expectEqual('Updated', og1.label);
const og = document.createElement('optgroup');
testing.expectEqual('', og.label);
}
</script>

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<script src="../../testing.js"></script>
<blockquote id="q1" cite="https://example.com/source">Quote</blockquote>
<script id="cite">
{
const q1 = document.getElementById('q1');
testing.expectEqual('https://example.com/source', q1.cite);
q1.cite = 'https://example.com/other';
testing.expectEqual('https://example.com/other', q1.cite);
const q2 = document.createElement('blockquote');
testing.expectEqual('', q2.cite);
}
</script>

View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<script src="../../testing.js"></script>
<table>
<tr>
<td id="td1" colspan="3" rowspan="2">Cell</td>
<td id="td2">Cell</td>
</tr>
</table>
<script id="colSpan">
{
const td1 = document.getElementById('td1');
testing.expectEqual(3, td1.colSpan);
td1.colSpan = 5;
testing.expectEqual(5, td1.colSpan);
const td2 = document.getElementById('td2');
testing.expectEqual(1, td2.colSpan);
}
</script>
<script id="rowSpan">
{
const td1 = document.getElementById('td1');
testing.expectEqual(2, td1.rowSpan);
td1.rowSpan = 4;
testing.expectEqual(4, td1.rowSpan);
const td2 = document.getElementById('td2');
testing.expectEqual(1, td2.rowSpan);
}
</script>

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<script src="../../testing.js"></script>
<time id="t1" datetime="2024-01-15">January 15</time>
<script id="dateTime">
{
const t = document.getElementById('t1');
testing.expectEqual('2024-01-15', t.dateTime);
t.dateTime = '2024-12-25T10:00';
testing.expectEqual('2024-12-25T10:00', t.dateTime);
const t2 = document.createElement('time');
testing.expectEqual('', t2.dateTime);
}
</script>

View File

@@ -1,4 +1,5 @@
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
@@ -14,6 +15,26 @@ pub fn asNode(self: *FieldSet) *Node {
return self.asElement().asNode();
}
pub fn getDisabled(self: *FieldSet) bool {
return self.asElement().getAttributeSafe(comptime .wrap("disabled")) != null;
}
pub fn setDisabled(self: *FieldSet, value: bool, page: *Page) !void {
if (value) {
try self.asElement().setAttributeSafe(comptime .wrap("disabled"), .wrap(""), page);
} else {
try self.asElement().removeAttribute(comptime .wrap("disabled"), page);
}
}
pub fn getName(self: *FieldSet) []const u8 {
return self.asElement().getAttributeSafe(comptime .wrap("name")) orelse "";
}
pub fn setName(self: *FieldSet, value: []const u8, page: *Page) !void {
try self.asElement().setAttributeSafe(comptime .wrap("name"), .wrap(value), page);
}
pub const JsApi = struct {
pub const bridge = js.Bridge(FieldSet);
@@ -22,4 +43,12 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
pub const disabled = bridge.accessor(FieldSet.getDisabled, FieldSet.setDisabled, .{});
pub const name = bridge.accessor(FieldSet.getName, FieldSet.setName, .{});
};
const testing = @import("../../../../testing.zig");
test "WebApi: HTML.FieldSet" {
try testing.htmlRunner("element/html/fieldset.html", .{});
}

View File

@@ -16,7 +16,9 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
@@ -31,6 +33,16 @@ pub fn asNode(self: *LI) *Node {
return self.asElement().asNode();
}
pub fn getValue(self: *LI) i32 {
const attr = self.asElement().getAttributeSafe(comptime .wrap("value")) orelse return 0;
return std.fmt.parseInt(i32, attr, 10) catch 0;
}
pub fn setValue(self: *LI, value: i32, page: *Page) !void {
const str = try std.fmt.allocPrint(page.call_arena, "{d}", .{value});
try self.asElement().setAttributeSafe(comptime .wrap("value"), .wrap(str), page);
}
pub const JsApi = struct {
pub const bridge = js.Bridge(LI);
@@ -39,4 +51,11 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
pub const value = bridge.accessor(LI.getValue, LI.setValue, .{});
};
const testing = @import("../../../../testing.zig");
test "WebApi: HTML.LI" {
try testing.htmlRunner("element/html/li.html", .{});
}

View File

@@ -1,4 +1,5 @@
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
@@ -14,6 +15,14 @@ pub fn asNode(self: *Label) *Node {
return self.asElement().asNode();
}
pub fn getHtmlFor(self: *Label) []const u8 {
return self.asElement().getAttributeSafe(comptime .wrap("for")) orelse "";
}
pub fn setHtmlFor(self: *Label, value: []const u8, page: *Page) !void {
try self.asElement().setAttributeSafe(comptime .wrap("for"), .wrap(value), page);
}
pub const JsApi = struct {
pub const bridge = js.Bridge(Label);
@@ -22,4 +31,11 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
pub const htmlFor = bridge.accessor(Label.getHtmlFor, Label.setHtmlFor, .{});
};
const testing = @import("../../../../testing.zig");
test "WebApi: HTML.Label" {
try testing.htmlRunner("element/html/label.html", .{});
}

View File

@@ -16,7 +16,9 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
@@ -31,6 +33,36 @@ pub fn asNode(self: *OL) *Node {
return self.asElement().asNode();
}
pub fn getStart(self: *OL) i32 {
const attr = self.asElement().getAttributeSafe(comptime .wrap("start")) orelse return 1;
return std.fmt.parseInt(i32, attr, 10) catch 1;
}
pub fn setStart(self: *OL, value: i32, page: *Page) !void {
const str = try std.fmt.allocPrint(page.call_arena, "{d}", .{value});
try self.asElement().setAttributeSafe(comptime .wrap("start"), .wrap(str), page);
}
pub fn getReversed(self: *OL) bool {
return self.asElement().getAttributeSafe(comptime .wrap("reversed")) != null;
}
pub fn setReversed(self: *OL, value: bool, page: *Page) !void {
if (value) {
try self.asElement().setAttributeSafe(comptime .wrap("reversed"), .wrap(""), page);
} else {
try self.asElement().removeAttribute(comptime .wrap("reversed"), page);
}
}
pub fn getType(self: *OL) []const u8 {
return self.asElement().getAttributeSafe(comptime .wrap("type")) orelse "";
}
pub fn setType(self: *OL, value: []const u8, page: *Page) !void {
try self.asElement().setAttributeSafe(comptime .wrap("type"), .wrap(value), page);
}
pub const JsApi = struct {
pub const bridge = js.Bridge(OL);
@@ -39,4 +71,13 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
pub const start = bridge.accessor(OL.getStart, OL.setStart, .{});
pub const reversed = bridge.accessor(OL.getReversed, OL.setReversed, .{});
pub const @"type" = bridge.accessor(OL.getType, OL.setType, .{});
};
const testing = @import("../../../../testing.zig");
test "WebApi: HTML.OL" {
try testing.htmlRunner("element/html/ol.html", .{});
}

View File

@@ -1,4 +1,5 @@
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
@@ -14,6 +15,26 @@ pub fn asNode(self: *OptGroup) *Node {
return self.asElement().asNode();
}
pub fn getDisabled(self: *OptGroup) bool {
return self.asElement().getAttributeSafe(comptime .wrap("disabled")) != null;
}
pub fn setDisabled(self: *OptGroup, value: bool, page: *Page) !void {
if (value) {
try self.asElement().setAttributeSafe(comptime .wrap("disabled"), .wrap(""), page);
} else {
try self.asElement().removeAttribute(comptime .wrap("disabled"), page);
}
}
pub fn getLabel(self: *OptGroup) []const u8 {
return self.asElement().getAttributeSafe(comptime .wrap("label")) orelse "";
}
pub fn setLabel(self: *OptGroup, value: []const u8, page: *Page) !void {
try self.asElement().setAttributeSafe(comptime .wrap("label"), .wrap(value), page);
}
pub const JsApi = struct {
pub const bridge = js.Bridge(OptGroup);
@@ -22,4 +43,12 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
pub const disabled = bridge.accessor(OptGroup.getDisabled, OptGroup.setDisabled, .{});
pub const label = bridge.accessor(OptGroup.getLabel, OptGroup.setLabel, .{});
};
const testing = @import("../../../../testing.zig");
test "WebApi: HTML.OptGroup" {
try testing.htmlRunner("element/html/optgroup.html", .{});
}

View File

@@ -1,5 +1,6 @@
const String = @import("../../../../string.zig").String;
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
@@ -17,6 +18,17 @@ pub fn asNode(self: *Quote) *Node {
return self.asElement().asNode();
}
pub fn getCite(self: *Quote, page: *Page) ![]const u8 {
const attr = self.asElement().getAttributeSafe(comptime .wrap("cite")) orelse return "";
if (attr.len == 0) return "";
const URL = @import("../../URL.zig");
return URL.resolve(page.call_arena, page.base(), attr, .{});
}
pub fn setCite(self: *Quote, value: []const u8, page: *Page) !void {
try self.asElement().setAttributeSafe(comptime .wrap("cite"), .wrap(value), page);
}
pub const JsApi = struct {
pub const bridge = js.Bridge(Quote);
@@ -25,4 +37,11 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
pub const cite = bridge.accessor(Quote.getCite, Quote.setCite, .{});
};
const testing = @import("../../../../testing.zig");
test "WebApi: HTML.Quote" {
try testing.htmlRunner("element/html/quote.html", .{});
}

View File

@@ -1,5 +1,7 @@
const std = @import("std");
const String = @import("../../../../string.zig").String;
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
@@ -17,6 +19,26 @@ pub fn asNode(self: *TableCell) *Node {
return self.asElement().asNode();
}
pub fn getColSpan(self: *TableCell) u32 {
const attr = self.asElement().getAttributeSafe(comptime .wrap("colspan")) orelse return 1;
return std.fmt.parseUnsigned(u32, attr, 10) catch 1;
}
pub fn setColSpan(self: *TableCell, value: u32, page: *Page) !void {
const str = try std.fmt.allocPrint(page.call_arena, "{d}", .{value});
try self.asElement().setAttributeSafe(comptime .wrap("colspan"), .wrap(str), page);
}
pub fn getRowSpan(self: *TableCell) u32 {
const attr = self.asElement().getAttributeSafe(comptime .wrap("rowspan")) orelse return 1;
return std.fmt.parseUnsigned(u32, attr, 10) catch 1;
}
pub fn setRowSpan(self: *TableCell, value: u32, page: *Page) !void {
const str = try std.fmt.allocPrint(page.call_arena, "{d}", .{value});
try self.asElement().setAttributeSafe(comptime .wrap("rowspan"), .wrap(str), page);
}
pub const JsApi = struct {
pub const bridge = js.Bridge(TableCell);
@@ -25,4 +47,12 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
pub const colSpan = bridge.accessor(TableCell.getColSpan, TableCell.setColSpan, .{});
pub const rowSpan = bridge.accessor(TableCell.getRowSpan, TableCell.setRowSpan, .{});
};
const testing = @import("../../../../testing.zig");
test "WebApi: HTML.TableCell" {
try testing.htmlRunner("element/html/tablecell.html", .{});
}

View File

@@ -1,4 +1,5 @@
const js = @import("../../../js/js.zig");
const Page = @import("../../../Page.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
@@ -14,6 +15,14 @@ pub fn asNode(self: *Time) *Node {
return self.asElement().asNode();
}
pub fn getDateTime(self: *Time) []const u8 {
return self.asElement().getAttributeSafe(comptime .wrap("datetime")) orelse "";
}
pub fn setDateTime(self: *Time, value: []const u8, page: *Page) !void {
try self.asElement().setAttributeSafe(comptime .wrap("datetime"), .wrap(value), page);
}
pub const JsApi = struct {
pub const bridge = js.Bridge(Time);
@@ -22,4 +31,11 @@ pub const JsApi = struct {
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
pub const dateTime = bridge.accessor(Time.getDateTime, Time.setDateTime, .{});
};
const testing = @import("../../../../testing.zig");
test "WebApi: HTML.Time" {
try testing.htmlRunner("element/html/time.html", .{});
}