Add HTMLPictureElement

Fix tag mapping for various types (including Source, which is Picture related).
This commit is contained in:
Karl Seguin
2026-02-09 15:38:41 +08:00
parent 0a410a5544
commit 926bd20281
8 changed files with 118 additions and 12 deletions

View File

@@ -2095,6 +2095,12 @@ pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const
attribute_iterator, attribute_iterator,
.{ ._proto = undefined, ._tag_name = String.init(undefined, "address", .{}) catch unreachable, ._tag = .address }, .{ ._proto = undefined, ._tag_name = String.init(undefined, "address", .{}) catch unreachable, ._tag = .address },
), ),
asUint("picture") => return self.createHtmlElementT(
Element.Html.Picture,
namespace,
attribute_iterator,
.{ ._proto = undefined },
),
else => {}, else => {},
}, },
8 => switch (@as(u64, @bitCast(name[0..8].*))) { 8 => switch (@as(u64, @bitCast(name[0..8].*))) {

View File

@@ -811,6 +811,7 @@ pub const JsApis = flattenTypes(&.{
@import("../webapi/element/html/Option.zig"), @import("../webapi/element/html/Option.zig"),
@import("../webapi/element/html/Output.zig"), @import("../webapi/element/html/Output.zig"),
@import("../webapi/element/html/Paragraph.zig"), @import("../webapi/element/html/Paragraph.zig"),
@import("../webapi/element/html/Picture.zig"),
@import("../webapi/element/html/Param.zig"), @import("../webapi/element/html/Param.zig"),
@import("../webapi/element/html/Pre.zig"), @import("../webapi/element/html/Pre.zig"),
@import("../webapi/element/html/Progress.zig"), @import("../webapi/element/html/Progress.zig"),

View File

@@ -0,0 +1,55 @@
<!DOCTYPE html>
<script src="../../testing.js"></script>
<!-- <script id="createElement">
{
const picture = document.createElement('picture');
testing.expectEqual('PICTURE', picture.tagName);
testing.expectEqual('[object HTMLPictureElement]', Object.prototype.toString.call(picture));
}
</script>
<script id="constructor_type">
{
const picture = document.createElement('picture');
testing.expectEqual(true, picture instanceof HTMLElement);
testing.expectEqual(true, picture instanceof Element);
testing.expectEqual(true, picture instanceof Node);
}
</script> -->
<picture id="inline-picture">
<source media="(min-width: 800px)" srcset="large.jpg">
<source media="(min-width: 400px)" srcset="medium.jpg">
<img src="small.jpg" alt="Test image">
</picture>
<script id="inline_picture">
{
const picture = document.getElementById('inline-picture');
testing.expectEqual('PICTURE', picture.tagName);
testing.expectEqual(3, picture.children.length);
const sources = picture.querySelectorAll('source');
testing.expectEqual(2, sources.length);
// const img = picture.querySelector('img');
// testing.expectEqual('IMG', img.tagName);
}
</script>
<!-- <script id="appendChild">
{
const picture = document.createElement('picture');
const source = document.createElement('source');
const img = document.createElement('img');
picture.appendChild(source);
picture.appendChild(img);
testing.expectEqual(2, picture.children.length);
testing.expectEqual('SOURCE', picture.children[0].tagName);
testing.expectEqual('IMG', picture.children[1].tagName);
}
</script>
-->

View File

@@ -235,6 +235,7 @@ pub fn getTagNameLower(self: *const Element) []const u8 {
.option => "option", .option => "option",
.output => "output", .output => "output",
.p => "p", .p => "p",
.picture => "picture",
.param => "param", .param => "param",
.pre => "pre", .pre => "pre",
.progress => "progress", .progress => "progress",
@@ -311,6 +312,7 @@ pub fn getTagNameSpec(self: *const Element, buf: []u8) []const u8 {
.option => "OPTION", .option => "OPTION",
.output => "OUTPUT", .output => "OUTPUT",
.p => "P", .p => "P",
.picture => "PICTURE",
.param => "PARAM", .param => "PARAM",
.pre => "PRE", .pre => "PRE",
.progress => "PROGRESS", .progress => "PROGRESS",
@@ -1221,26 +1223,27 @@ pub fn getTag(self: *const Element) Tag {
.data => .data, .data => .data,
.datalist => .datalist, .datalist => .datalist,
.dialog => .dialog, .dialog => .dialog,
.directory => .unknown, .directory => .directory,
.iframe => .iframe, .iframe => .iframe,
.img => .img, .img => .img,
.br => .br, .br => .br,
.button => .button, .button => .button,
.canvas => .canvas, .canvas => .canvas,
.fieldset => .fieldset, .fieldset => .fieldset,
.font => .unknown, .font => .font,
.heading => |h| h._tag, .heading => |h| h._tag,
.label => .unknown, .label => .label,
.legend => .unknown, .legend => .legend,
.li => .li, .li => .li,
.map => .unknown, .map => .map,
.ul => .ul, .ul => .ul,
.ol => .ol, .ol => .ol,
.object => .unknown, .object => .object,
.optgroup => .optgroup, .optgroup => .optgroup,
.output => .unknown, .output => .output,
.param => .unknown, .picture => .picture,
.pre => .unknown, .param => .param,
.pre => .pre,
.generic => |g| g._tag, .generic => |g| g._tag,
.media => |m| switch (m._type) { .media => |m| switch (m._type) {
.audio => .audio, .audio => .audio,
@@ -1254,7 +1257,7 @@ pub fn getTag(self: *const Element) Tag {
.script => .script, .script => .script,
.select => .select, .select => .select,
.slot => .slot, .slot => .slot,
.source => .unknown, .source => .source,
.span => .span, .span => .span,
.option => .option, .option => .option,
.table => .table, .table => .table,
@@ -1266,7 +1269,7 @@ pub fn getTag(self: *const Element) Tag {
.template => .template, .template => .template,
.textarea => .textarea, .textarea => .textarea,
.time => .time, .time => .time,
.track => .unknown, .track => .track,
.input => .input, .input => .input,
.link => .link, .link => .link,
.meta => .meta, .meta => .meta,
@@ -1313,6 +1316,7 @@ pub const Tag = enum {
dfn, dfn,
dialog, dialog,
div, div,
directory,
dl, dl,
dt, dt,
embed, embed,
@@ -1321,6 +1325,7 @@ pub const Tag = enum {
fieldset, fieldset,
figure, figure,
form, form,
font,
footer, footer,
g, g,
h1, h1,
@@ -1340,10 +1345,13 @@ pub const Tag = enum {
img, img,
input, input,
ins, ins,
label,
legend,
li, li,
line, line,
link, link,
main, main,
map,
marquee, marquee,
media, media,
menu, menu,
@@ -1359,8 +1367,10 @@ pub const Tag = enum {
p, p,
path, path,
param, param,
picture,
polygon, polygon,
polyline, polyline,
pre,
progress, progress,
quote, quote,
rect, rect,

View File

@@ -218,7 +218,7 @@ fn getDefaultDisplay(element: *const Element) []const u8 {
.html => |html| { .html => |html| {
return switch (html._type) { return switch (html._type) {
.anchor, .br, .span, .label, .time, .font, .mod, .quote => "inline", .anchor, .br, .span, .label, .time, .font, .mod, .quote => "inline",
.body, .div, .p, .heading, .form, .button, .canvas, .dialog, .embed, .head, .html, .hr, .iframe, .img, .input, .li, .link, .meta, .ol, .option, .script, .select, .slot, .style, .template, .textarea, .title, .ul, .media, .area, .base, .datalist, .directory, .fieldset, .legend, .map, .meter, .object, .optgroup, .output, .param, .pre, .progress, .source, .table, .table_caption, .table_cell, .table_col, .table_row, .table_section, .track => "block", .body, .div, .p, .heading, .form, .button, .canvas, .dialog, .embed, .head, .html, .hr, .iframe, .img, .input, .li, .link, .meta, .ol, .option, .script, .select, .slot, .style, .template, .textarea, .title, .ul, .media, .area, .base, .datalist, .directory, .fieldset, .legend, .map, .meter, .object, .optgroup, .output, .param, .picture, .pre, .progress, .source, .table, .table_caption, .table_cell, .table_col, .table_row, .table_section, .track => "block",
.generic, .custom, .unknown, .data => blk: { .generic, .custom, .unknown, .data => blk: {
const tag = element.getTagNameLower(); const tag = element.getTagNameLower();
if (isInlineTag(tag)) break :blk "inline"; if (isInlineTag(tag)) break :blk "inline";

View File

@@ -69,6 +69,7 @@ pub const OptGroup = @import("html/OptGroup.zig");
pub const Option = @import("html/Option.zig"); pub const Option = @import("html/Option.zig");
pub const Output = @import("html/Output.zig"); pub const Output = @import("html/Output.zig");
pub const Paragraph = @import("html/Paragraph.zig"); pub const Paragraph = @import("html/Paragraph.zig");
pub const Picture = @import("html/Picture.zig");
pub const Param = @import("html/Param.zig"); pub const Param = @import("html/Param.zig");
pub const Pre = @import("html/Pre.zig"); pub const Pre = @import("html/Pre.zig");
pub const Progress = @import("html/Progress.zig"); pub const Progress = @import("html/Progress.zig");
@@ -145,6 +146,7 @@ pub const Type = union(enum) {
option: *Option, option: *Option,
output: *Output, output: *Output,
p: *Paragraph, p: *Paragraph,
picture: *Picture,
param: *Param, param: *Param,
pre: *Pre, pre: *Pre,
progress: *Progress, progress: *Progress,

View File

@@ -0,0 +1,30 @@
const js = @import("../../../js/js.zig");
const Node = @import("../../Node.zig");
const Element = @import("../../Element.zig");
const HtmlElement = @import("../Html.zig");
const Picture = @This();
_proto: *HtmlElement,
pub fn asElement(self: *Picture) *Element {
return self._proto._proto;
}
pub fn asNode(self: *Picture) *Node {
return self.asElement().asNode();
}
pub const JsApi = struct {
pub const bridge = js.Bridge(Picture);
pub const Meta = struct {
pub const name = "HTMLPictureElement";
pub const prototype_chain = bridge.prototypeChain();
pub var class_id: bridge.ClassId = undefined;
};
};
const testing = @import("../../../../testing.zig");
test "WebApi: Picture" {
try testing.htmlRunner("element/html/picture.html", .{});
}

View File

@@ -38,6 +38,8 @@ pub fn main() !void {
.user_agent_suffix = "internal-tester", .user_agent_suffix = "internal-tester",
}, },
} }); } });
defer config.deinit(allocator);
var app = try lp.App.init(allocator, &config); var app = try lp.App.init(allocator, &config);
defer app.deinit(); defer app.deinit();