mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-09-16 07:48:35 -04:00
Merge pull request #3128 from lightpanda-io/element-bare-tag
mem: Switch Element's _type to a bare tag
This commit is contained in:
18 files changed
+767
-478
No files matched your search
@@ -474,7 +474,7 @@ pub fn svgElement(self: *Factory, tag_name: []const u8, child: anytype) !*@TypeO
|
||||
const svg_ptr = chain.get(i);
|
||||
svg_ptr.* = .{
|
||||
._tag_name = try String.init(self._arena, tag_name, .{}),
|
||||
._type = unionInit(Element.Svg.Type, chain.get(i + 1)),
|
||||
._type = typeInit(Element.Svg, chain.get(i + 1)),
|
||||
};
|
||||
setProto(svg_ptr, chain.get(i - 1));
|
||||
} else {
|
||||
|
||||
@@ -261,7 +261,7 @@ fn dumpSlotContent(slot: *Slot, opts: Opts, writer: *std.Io.Writer, frame: *Fram
|
||||
|
||||
fn isVoidElement(el: *const Node.Element) bool {
|
||||
return switch (el._type) {
|
||||
.html => |html| switch (html._type) {
|
||||
.html => switch (el.subtype(Node.Element.Html)._type) {
|
||||
.br, .hr, .img, .input, .link, .meta => true,
|
||||
else => false,
|
||||
},
|
||||
|
||||
@@ -208,7 +208,7 @@ fn hasClickActivationBehavior(node: *Node) bool {
|
||||
return switch (html_element._type) {
|
||||
.anchor => element.getAttributeSafe(comptime .wrap("href")) != null,
|
||||
.input, .button, .select, .textarea, .label => true,
|
||||
.generic => |generic| generic._tag == .summary,
|
||||
.generic => html_element.subtype(Element.Html.Generic)._tag == .summary,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
@@ -328,7 +328,8 @@ pub fn handleClick(frame: *Frame, target: *Node) !void {
|
||||
const html_element = element.is(Element.Html) orelse return;
|
||||
|
||||
switch (html_element._type) {
|
||||
.anchor => |anchor| {
|
||||
.anchor => {
|
||||
const anchor = html_element.subtype(Element.Html.Anchor);
|
||||
const href = element.getAttributeSafe(comptime .wrap("href")) orelse return;
|
||||
if (href.len == 0) {
|
||||
return;
|
||||
@@ -363,7 +364,8 @@ pub fn handleClick(frame: *Frame, target: *Node) !void {
|
||||
.kind = .{ .push = null },
|
||||
}, .{ .anchor = target_frame });
|
||||
},
|
||||
.input => |input| {
|
||||
.input => {
|
||||
const input = html_element.subtype(Element.Html.Input);
|
||||
try element.focus(frame);
|
||||
// Per HTML §4.10.18.6.4 "Image Button state (type=image)", clicking an
|
||||
// image button submits its form. The form-data set already gets the
|
||||
@@ -373,14 +375,16 @@ pub fn handleClick(frame: *Frame, target: *Node) !void {
|
||||
return frame.submitForm(element, input.getForm(frame), .{});
|
||||
}
|
||||
},
|
||||
.button => |button| {
|
||||
.button => {
|
||||
const button = html_element.subtype(Element.Html.Button);
|
||||
try element.focus(frame);
|
||||
if (std.mem.eql(u8, button.getType(), "submit")) {
|
||||
return frame.submitForm(element, button.getForm(frame), .{});
|
||||
}
|
||||
},
|
||||
.select, .textarea => try element.focus(frame),
|
||||
.label => |label| {
|
||||
.label => {
|
||||
const label = html_element.subtype(Element.Html.Label);
|
||||
// Per HTML §4.10.4 "The label element", a label's activation
|
||||
// behavior is to run the synthetic click activation steps on the
|
||||
// labeled control. Mirrors Chrome's HTMLLabelElement::DefaultEventHandler.
|
||||
@@ -388,8 +392,8 @@ pub fn handleClick(frame: *Frame, target: *Node) !void {
|
||||
const control_html = control.is(Element.Html) orelse return;
|
||||
try control_html.click(frame);
|
||||
},
|
||||
.generic => |generic| {
|
||||
switch (generic._tag) {
|
||||
.generic => {
|
||||
switch (html_element.subtype(Element.Html.Generic)._tag) {
|
||||
.summary => {
|
||||
const parent_el = target.parentElement() orelse return;
|
||||
const details = parent_el.is(Element.Html.Details) orelse return;
|
||||
|
||||
@@ -1267,7 +1267,15 @@ pub fn resolveValue(value: anytype) Resolved {
|
||||
// (e.g. CData); the type maps the tag to the member's type.
|
||||
if (comptime @typeInfo(@TypeOf(value._type)) == .@"enum" and @hasDecl(T, "Subtype")) {
|
||||
switch (value._type) {
|
||||
inline else => |tag| return resolveValue(value.subtype(T.Subtype(tag))),
|
||||
inline else => |tag| {
|
||||
const S = T.Subtype(tag);
|
||||
if (S == T) {
|
||||
// A tag can map to the type itself (e.g. Media.generic);
|
||||
// the value is already the most specific type.
|
||||
return resolveT(T, value);
|
||||
}
|
||||
return resolveValue(value.subtype(S));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -600,7 +600,7 @@ fn getTemplateContentsCallback(ctx: *anyopaque, target_ref: *anyopaque) callconv
|
||||
|
||||
fn _getTemplateContentsCallback(self: *Parser, node: *Node) !*anyopaque {
|
||||
const element = node.as(Element);
|
||||
const template = element._type.html.is(Element.Html.Template) orelse unreachable;
|
||||
const template = element.subtype(Element.Html).is(Element.Html.Template) orelse unreachable;
|
||||
const content_node = template.getContent().asNode();
|
||||
|
||||
// Create a ParsedNode wrapper for the content DocumentFragment
|
||||
|
||||
+274
-245
@@ -115,15 +115,35 @@ _attributes: Attribute.List = .{},
|
||||
// work to resolve the proto).
|
||||
_proto_canary: if (lp.IS_DEBUG) *Node else void = undefined,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
html: *Html,
|
||||
svg: *Svg,
|
||||
pub const Type = enum(u8) {
|
||||
html,
|
||||
svg,
|
||||
};
|
||||
|
||||
pub fn Subtype(comptime tag: Type) type {
|
||||
return switch (tag) {
|
||||
.html => Html,
|
||||
.svg => Svg,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn subtype(self: *const Element, comptime T: type) *T {
|
||||
const offset = comptime Factory.chainOffsetOf(T, T) - Factory.chainOffsetOf(T, Element);
|
||||
const sub: *T = @ptrFromInt(@intFromPtr(self) + offset);
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
// This pointer dance only works because the factory allocates the chain
|
||||
// in a contiguous block of memory. In debug, we assert this holds via
|
||||
// the _proto_canary back pointer.
|
||||
std.debug.assert(Factory.protoOf(sub) == self);
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
pub fn is(self: *Element, comptime T: type) ?*T {
|
||||
const type_name = @typeName(T);
|
||||
switch (self._type) {
|
||||
.html => |el| {
|
||||
.html => {
|
||||
const el = self.subtype(Html);
|
||||
if (T == Html) {
|
||||
return el;
|
||||
}
|
||||
@@ -131,7 +151,8 @@ pub fn is(self: *Element, comptime T: type) ?*T {
|
||||
return el.is(T);
|
||||
}
|
||||
},
|
||||
.svg => |svg| {
|
||||
.svg => {
|
||||
const svg = self.subtype(Svg);
|
||||
if (T == Svg) {
|
||||
return svg;
|
||||
}
|
||||
@@ -178,177 +199,183 @@ pub fn isEqualNode(self: *Element, other: *Element) bool {
|
||||
|
||||
pub fn getTagNameLower(self: *const Element) []const u8 {
|
||||
switch (self._type) {
|
||||
.html => |he| switch (he._type) {
|
||||
.custom => |ce| {
|
||||
@branchHint(.unlikely);
|
||||
return ce._tag_name.str();
|
||||
},
|
||||
else => return switch (he._type) {
|
||||
.anchor => "a",
|
||||
.area => "area",
|
||||
.base => "base",
|
||||
.body => "body",
|
||||
.br => "br",
|
||||
.button => "button",
|
||||
.canvas => "canvas",
|
||||
.custom => |e| e._tag_name.str(),
|
||||
.data => "data",
|
||||
.datalist => "datalist",
|
||||
.details => "details",
|
||||
.dialog => "dialog",
|
||||
.directory => "dir",
|
||||
.div => "div",
|
||||
.dl => "dl",
|
||||
.embed => "embed",
|
||||
.fieldset => "fieldset",
|
||||
.font => "font",
|
||||
.frameset => "frameset",
|
||||
.form => "form",
|
||||
.generic => |e| e._tag_name.str(),
|
||||
.heading => |e| e._tag_name.str(),
|
||||
.head => "head",
|
||||
.html => "html",
|
||||
.hr => "hr",
|
||||
.iframe => "iframe",
|
||||
.img => "img",
|
||||
.input => "input",
|
||||
.label => "label",
|
||||
.legend => "legend",
|
||||
.li => "li",
|
||||
.link => "link",
|
||||
.map => "map",
|
||||
.marquee => "marquee",
|
||||
.media => |m| switch (m._type) {
|
||||
.audio => "audio",
|
||||
.video => "video",
|
||||
.generic => "media",
|
||||
.html => {
|
||||
const he = self.subtype(Html);
|
||||
switch (he._type) {
|
||||
.custom => {
|
||||
@branchHint(.unlikely);
|
||||
return he.subtype(Html.Custom)._tag_name.str();
|
||||
},
|
||||
.meta => "meta",
|
||||
.meter => "meter",
|
||||
.mod => |e| e._tag_name.str(),
|
||||
.object => "object",
|
||||
.ol => "ol",
|
||||
.optgroup => "optgroup",
|
||||
.option => "option",
|
||||
.output => "output",
|
||||
.p => "p",
|
||||
.picture => "picture",
|
||||
.param => "param",
|
||||
.pre => "pre",
|
||||
.progress => "progress",
|
||||
.quote => |e| e._tag_name.str(),
|
||||
.script => "script",
|
||||
.select => "select",
|
||||
.slot => "slot",
|
||||
.source => "source",
|
||||
.span => "span",
|
||||
.style => "style",
|
||||
.table => "table",
|
||||
.table_caption => "caption",
|
||||
.table_cell => |e| e._tag_name.str(),
|
||||
.table_col => |e| e._tag_name.str(),
|
||||
.table_row => "tr",
|
||||
.table_section => |e| e._tag_name.str(),
|
||||
.template => "template",
|
||||
.textarea => "textarea",
|
||||
.time => "time",
|
||||
.title => "title",
|
||||
.track => "track",
|
||||
.ul => "ul",
|
||||
.unknown => |e| e._tag_name.str(),
|
||||
},
|
||||
else => return switch (he._type) {
|
||||
.anchor => "a",
|
||||
.area => "area",
|
||||
.base => "base",
|
||||
.body => "body",
|
||||
.br => "br",
|
||||
.button => "button",
|
||||
.canvas => "canvas",
|
||||
.custom => he.subtype(Html.Custom)._tag_name.str(),
|
||||
.data => "data",
|
||||
.datalist => "datalist",
|
||||
.details => "details",
|
||||
.dialog => "dialog",
|
||||
.directory => "dir",
|
||||
.div => "div",
|
||||
.dl => "dl",
|
||||
.embed => "embed",
|
||||
.fieldset => "fieldset",
|
||||
.font => "font",
|
||||
.frameset => "frameset",
|
||||
.form => "form",
|
||||
.generic => he.subtype(Html.Generic)._tag_name.str(),
|
||||
.heading => he.subtype(Html.Heading)._tag_name.str(),
|
||||
.head => "head",
|
||||
.html => "html",
|
||||
.hr => "hr",
|
||||
.iframe => "iframe",
|
||||
.img => "img",
|
||||
.input => "input",
|
||||
.label => "label",
|
||||
.legend => "legend",
|
||||
.li => "li",
|
||||
.link => "link",
|
||||
.map => "map",
|
||||
.marquee => "marquee",
|
||||
.media => switch (he.subtype(Html.Media)._type) {
|
||||
.audio => "audio",
|
||||
.video => "video",
|
||||
.generic => "media",
|
||||
},
|
||||
.meta => "meta",
|
||||
.meter => "meter",
|
||||
.mod => he.subtype(Html.Mod)._tag_name.str(),
|
||||
.object => "object",
|
||||
.ol => "ol",
|
||||
.optgroup => "optgroup",
|
||||
.option => "option",
|
||||
.output => "output",
|
||||
.p => "p",
|
||||
.picture => "picture",
|
||||
.param => "param",
|
||||
.pre => "pre",
|
||||
.progress => "progress",
|
||||
.quote => he.subtype(Html.Quote)._tag_name.str(),
|
||||
.script => "script",
|
||||
.select => "select",
|
||||
.slot => "slot",
|
||||
.source => "source",
|
||||
.span => "span",
|
||||
.style => "style",
|
||||
.table => "table",
|
||||
.table_caption => "caption",
|
||||
.table_cell => he.subtype(Html.TableCell)._tag_name.str(),
|
||||
.table_col => he.subtype(Html.TableCol)._tag_name.str(),
|
||||
.table_row => "tr",
|
||||
.table_section => he.subtype(Html.TableSection)._tag_name.str(),
|
||||
.template => "template",
|
||||
.textarea => "textarea",
|
||||
.time => "time",
|
||||
.title => "title",
|
||||
.track => "track",
|
||||
.ul => "ul",
|
||||
.unknown => he.subtype(Html.Unknown)._tag_name.str(),
|
||||
},
|
||||
}
|
||||
},
|
||||
.svg => |svg| return svg._tag_name.str(),
|
||||
.svg => return self.subtype(Svg)._tag_name.str(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getTagNameSpec(self: *const Element, buf: []u8) []const u8 {
|
||||
return switch (self._type) {
|
||||
.html => |he| switch (he._type) {
|
||||
.anchor => "A",
|
||||
.area => "AREA",
|
||||
.base => "BASE",
|
||||
.body => "BODY",
|
||||
.br => "BR",
|
||||
.button => "BUTTON",
|
||||
.canvas => "CANVAS",
|
||||
.custom => |e| upperTagName(&e._tag_name, buf),
|
||||
.data => "DATA",
|
||||
.datalist => "DATALIST",
|
||||
.details => "DETAILS",
|
||||
.dialog => "DIALOG",
|
||||
.directory => "DIR",
|
||||
.div => "DIV",
|
||||
.dl => "DL",
|
||||
.embed => "EMBED",
|
||||
.fieldset => "FIELDSET",
|
||||
.font => "FONT",
|
||||
.frameset => "FRAMESET",
|
||||
.form => "FORM",
|
||||
.generic => |e| upperTagName(&e._tag_name, buf),
|
||||
.heading => |e| upperTagName(&e._tag_name, buf),
|
||||
.head => "HEAD",
|
||||
.html => "HTML",
|
||||
.hr => "HR",
|
||||
.iframe => "IFRAME",
|
||||
.img => "IMG",
|
||||
.input => "INPUT",
|
||||
.label => "LABEL",
|
||||
.legend => "LEGEND",
|
||||
.li => "LI",
|
||||
.link => "LINK",
|
||||
.map => "MAP",
|
||||
.marquee => "MARQUEE",
|
||||
.meta => "META",
|
||||
.media => |m| switch (m._type) {
|
||||
.audio => "AUDIO",
|
||||
.video => "VIDEO",
|
||||
.generic => "MEDIA",
|
||||
},
|
||||
.meter => "METER",
|
||||
.mod => |e| upperTagName(&e._tag_name, buf),
|
||||
.object => "OBJECT",
|
||||
.ol => "OL",
|
||||
.optgroup => "OPTGROUP",
|
||||
.option => "OPTION",
|
||||
.output => "OUTPUT",
|
||||
.p => "P",
|
||||
.picture => "PICTURE",
|
||||
.param => "PARAM",
|
||||
.pre => "PRE",
|
||||
.progress => "PROGRESS",
|
||||
.quote => |e| upperTagName(&e._tag_name, buf),
|
||||
.script => "SCRIPT",
|
||||
.select => "SELECT",
|
||||
.slot => "SLOT",
|
||||
.source => "SOURCE",
|
||||
.span => "SPAN",
|
||||
.style => "STYLE",
|
||||
.table => "TABLE",
|
||||
.table_caption => "CAPTION",
|
||||
.table_cell => |e| upperTagName(&e._tag_name, buf),
|
||||
.table_col => |e| upperTagName(&e._tag_name, buf),
|
||||
.table_row => "TR",
|
||||
.table_section => |e| upperTagName(&e._tag_name, buf),
|
||||
.template => "TEMPLATE",
|
||||
.textarea => "TEXTAREA",
|
||||
.time => "TIME",
|
||||
.title => "TITLE",
|
||||
.track => "TRACK",
|
||||
.ul => "UL",
|
||||
.unknown => |e| switch (self._namespace) {
|
||||
.html => upperTagName(&e._tag_name, buf),
|
||||
.svg, .xml, .mathml, .unknown, .null => e._tag_name.str(),
|
||||
},
|
||||
.html => blk: {
|
||||
const he = self.subtype(Html);
|
||||
break :blk switch (he._type) {
|
||||
.anchor => "A",
|
||||
.area => "AREA",
|
||||
.base => "BASE",
|
||||
.body => "BODY",
|
||||
.br => "BR",
|
||||
.button => "BUTTON",
|
||||
.canvas => "CANVAS",
|
||||
.custom => upperTagName(&he.subtype(Html.Custom)._tag_name, buf),
|
||||
.data => "DATA",
|
||||
.datalist => "DATALIST",
|
||||
.details => "DETAILS",
|
||||
.dialog => "DIALOG",
|
||||
.directory => "DIR",
|
||||
.div => "DIV",
|
||||
.dl => "DL",
|
||||
.embed => "EMBED",
|
||||
.fieldset => "FIELDSET",
|
||||
.font => "FONT",
|
||||
.frameset => "FRAMESET",
|
||||
.form => "FORM",
|
||||
.generic => upperTagName(&he.subtype(Html.Generic)._tag_name, buf),
|
||||
.heading => upperTagName(&he.subtype(Html.Heading)._tag_name, buf),
|
||||
.head => "HEAD",
|
||||
.html => "HTML",
|
||||
.hr => "HR",
|
||||
.iframe => "IFRAME",
|
||||
.img => "IMG",
|
||||
.input => "INPUT",
|
||||
.label => "LABEL",
|
||||
.legend => "LEGEND",
|
||||
.li => "LI",
|
||||
.link => "LINK",
|
||||
.map => "MAP",
|
||||
.marquee => "MARQUEE",
|
||||
.meta => "META",
|
||||
.media => switch (he.subtype(Html.Media)._type) {
|
||||
.audio => "AUDIO",
|
||||
.video => "VIDEO",
|
||||
.generic => "MEDIA",
|
||||
},
|
||||
.meter => "METER",
|
||||
.mod => upperTagName(&he.subtype(Html.Mod)._tag_name, buf),
|
||||
.object => "OBJECT",
|
||||
.ol => "OL",
|
||||
.optgroup => "OPTGROUP",
|
||||
.option => "OPTION",
|
||||
.output => "OUTPUT",
|
||||
.p => "P",
|
||||
.picture => "PICTURE",
|
||||
.param => "PARAM",
|
||||
.pre => "PRE",
|
||||
.progress => "PROGRESS",
|
||||
.quote => upperTagName(&he.subtype(Html.Quote)._tag_name, buf),
|
||||
.script => "SCRIPT",
|
||||
.select => "SELECT",
|
||||
.slot => "SLOT",
|
||||
.source => "SOURCE",
|
||||
.span => "SPAN",
|
||||
.style => "STYLE",
|
||||
.table => "TABLE",
|
||||
.table_caption => "CAPTION",
|
||||
.table_cell => upperTagName(&he.subtype(Html.TableCell)._tag_name, buf),
|
||||
.table_col => upperTagName(&he.subtype(Html.TableCol)._tag_name, buf),
|
||||
.table_row => "TR",
|
||||
.table_section => upperTagName(&he.subtype(Html.TableSection)._tag_name, buf),
|
||||
.template => "TEMPLATE",
|
||||
.textarea => "TEXTAREA",
|
||||
.time => "TIME",
|
||||
.title => "TITLE",
|
||||
.track => "TRACK",
|
||||
.ul => "UL",
|
||||
.unknown => switch (self._namespace) {
|
||||
.html => upperTagName(&he.subtype(Html.Unknown)._tag_name, buf),
|
||||
.svg, .xml, .mathml, .unknown, .null => he.subtype(Html.Unknown)._tag_name.str(),
|
||||
},
|
||||
};
|
||||
},
|
||||
.svg => |svg| svg._tag_name.str(),
|
||||
.svg => self.subtype(Svg)._tag_name.str(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getTagNameDump(self: *const Element) []const u8 {
|
||||
switch (self._type) {
|
||||
.html => return self.getTagNameLower(),
|
||||
.svg => |svg| return svg._tag_name.str(),
|
||||
.svg => return self.subtype(Svg)._tag_name.str(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1916,81 +1943,84 @@ fn upperTagName(tag_name: *String, buf: []u8) []const u8 {
|
||||
|
||||
pub fn getTag(self: *const Element) Tag {
|
||||
return switch (self._type) {
|
||||
.html => |he| switch (he._type) {
|
||||
.anchor => .anchor,
|
||||
.area => .area,
|
||||
.base => .base,
|
||||
.div => .div,
|
||||
.dl => .dl,
|
||||
.embed => .embed,
|
||||
.form => .form,
|
||||
.p => .p,
|
||||
.custom => .custom,
|
||||
.data => .data,
|
||||
.datalist => .datalist,
|
||||
.details => .details,
|
||||
.dialog => .dialog,
|
||||
.directory => .directory,
|
||||
.iframe => .iframe,
|
||||
.img => .img,
|
||||
.br => .br,
|
||||
.button => .button,
|
||||
.canvas => .canvas,
|
||||
.fieldset => .fieldset,
|
||||
.font => .font,
|
||||
.frameset => .frameset,
|
||||
.heading => |h| h._tag,
|
||||
.label => .label,
|
||||
.legend => .legend,
|
||||
.li => .li,
|
||||
.map => .map,
|
||||
.marquee => .marquee,
|
||||
.ul => .ul,
|
||||
.ol => .ol,
|
||||
.object => .object,
|
||||
.optgroup => .optgroup,
|
||||
.output => .output,
|
||||
.picture => .picture,
|
||||
.param => .param,
|
||||
.pre => .pre,
|
||||
.generic => |g| g._tag,
|
||||
.media => |m| switch (m._type) {
|
||||
.audio => .audio,
|
||||
.video => .video,
|
||||
.generic => .media,
|
||||
},
|
||||
.meter => .meter,
|
||||
.mod => |m| m._tag,
|
||||
.progress => .progress,
|
||||
.quote => |q| q._tag,
|
||||
.script => .script,
|
||||
.select => .select,
|
||||
.slot => .slot,
|
||||
.source => .source,
|
||||
.span => .span,
|
||||
.option => .option,
|
||||
.table => .table,
|
||||
.table_caption => .caption,
|
||||
.table_cell => |tc| tc._tag,
|
||||
.table_col => |tc| tc._tag,
|
||||
.table_row => .tr,
|
||||
.table_section => |ts| ts._tag,
|
||||
.template => .template,
|
||||
.textarea => .textarea,
|
||||
.time => .time,
|
||||
.track => .track,
|
||||
.input => .input,
|
||||
.link => .link,
|
||||
.meta => .meta,
|
||||
.hr => .hr,
|
||||
.style => .style,
|
||||
.title => .title,
|
||||
.body => .body,
|
||||
.html => .html,
|
||||
.head => .head,
|
||||
.unknown => .unknown,
|
||||
.html => blk: {
|
||||
const he = self.subtype(Html);
|
||||
break :blk switch (he._type) {
|
||||
.anchor => .anchor,
|
||||
.area => .area,
|
||||
.base => .base,
|
||||
.div => .div,
|
||||
.dl => .dl,
|
||||
.embed => .embed,
|
||||
.form => .form,
|
||||
.p => .p,
|
||||
.custom => .custom,
|
||||
.data => .data,
|
||||
.datalist => .datalist,
|
||||
.details => .details,
|
||||
.dialog => .dialog,
|
||||
.directory => .directory,
|
||||
.iframe => .iframe,
|
||||
.img => .img,
|
||||
.br => .br,
|
||||
.button => .button,
|
||||
.canvas => .canvas,
|
||||
.fieldset => .fieldset,
|
||||
.font => .font,
|
||||
.frameset => .frameset,
|
||||
.heading => he.subtype(Html.Heading)._tag,
|
||||
.label => .label,
|
||||
.legend => .legend,
|
||||
.li => .li,
|
||||
.map => .map,
|
||||
.marquee => .marquee,
|
||||
.ul => .ul,
|
||||
.ol => .ol,
|
||||
.object => .object,
|
||||
.optgroup => .optgroup,
|
||||
.output => .output,
|
||||
.picture => .picture,
|
||||
.param => .param,
|
||||
.pre => .pre,
|
||||
.generic => he.subtype(Html.Generic)._tag,
|
||||
.media => switch (he.subtype(Html.Media)._type) {
|
||||
.audio => .audio,
|
||||
.video => .video,
|
||||
.generic => .media,
|
||||
},
|
||||
.meter => .meter,
|
||||
.mod => he.subtype(Html.Mod)._tag,
|
||||
.progress => .progress,
|
||||
.quote => he.subtype(Html.Quote)._tag,
|
||||
.script => .script,
|
||||
.select => .select,
|
||||
.slot => .slot,
|
||||
.source => .source,
|
||||
.span => .span,
|
||||
.option => .option,
|
||||
.table => .table,
|
||||
.table_caption => .caption,
|
||||
.table_cell => he.subtype(Html.TableCell)._tag,
|
||||
.table_col => he.subtype(Html.TableCol)._tag,
|
||||
.table_row => .tr,
|
||||
.table_section => he.subtype(Html.TableSection)._tag,
|
||||
.template => .template,
|
||||
.textarea => .textarea,
|
||||
.time => .time,
|
||||
.track => .track,
|
||||
.input => .input,
|
||||
.link => .link,
|
||||
.meta => .meta,
|
||||
.hr => .hr,
|
||||
.style => .style,
|
||||
.title => .title,
|
||||
.body => .body,
|
||||
.html => .html,
|
||||
.head => .head,
|
||||
.unknown => .unknown,
|
||||
};
|
||||
},
|
||||
.svg => |se| se.getTag(),
|
||||
.svg => self.subtype(Svg).getTag(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2353,22 +2383,21 @@ pub const Build = struct {
|
||||
// Calls `func_name` with `args` on the most specific type where it is
|
||||
// implement. This could be on the Element itself.
|
||||
pub fn call(self: *const Element, comptime func_name: []const u8, args: anytype) !bool {
|
||||
inline for (@typeInfo(Element.Type).@"union".fields) |f| {
|
||||
if (@field(Element.Type, f.name) == self._type) {
|
||||
// The inner type implements this function. Call it and we're done.
|
||||
const S = reflect.Struct(f.type);
|
||||
switch (self._type) {
|
||||
inline else => |tag| {
|
||||
const S = Subtype(tag);
|
||||
if (@hasDecl(S, "Build")) {
|
||||
// The inner type has its own "call" method. Defer to it.
|
||||
if (@hasDecl(S.Build, "call")) {
|
||||
const sub = @field(self._type, f.name);
|
||||
return S.Build.call(sub, func_name, args);
|
||||
return S.Build.call(self.subtype(S), func_name, args);
|
||||
}
|
||||
|
||||
// The inner type implements this function. Call it and we're done.
|
||||
if (@hasDecl(f.type, func_name)) {
|
||||
return @call(.auto, @field(f.type, func_name), args);
|
||||
if (@hasDecl(S, func_name)) {
|
||||
return @call(.auto, @field(S, func_name), args);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if (@hasDecl(Element.Build, func_name)) {
|
||||
|
||||
@@ -407,7 +407,7 @@ pub fn NodeLive(comptime mode: Mode) type {
|
||||
|
||||
fn isFormControl(el: *Element) bool {
|
||||
if (el._type != .html) return false;
|
||||
const html = el._type.html;
|
||||
const html = el.subtype(Element.Html);
|
||||
return switch (html._type) {
|
||||
.input, .button, .select, .textarea => true,
|
||||
else => false,
|
||||
|
||||
@@ -802,8 +802,8 @@ fn getDefaultPropertyValue(self: *const CSSStyleDeclaration, name: String) []con
|
||||
|
||||
fn getDefaultDisplay(element: *const Element) []const u8 {
|
||||
switch (element._type) {
|
||||
.html => |html| {
|
||||
return switch (html._type) {
|
||||
.html => {
|
||||
return switch (element.subtype(Element.Html)._type) {
|
||||
.anchor, .br, .span, .label, .time, .font, .mod, .quote => "inline",
|
||||
.body, .div, .dl, .p, .heading, .form, .button, .canvas, .details, .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, .frameset, .legend, .map, .marquee, .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: {
|
||||
@@ -835,8 +835,8 @@ fn isInlineTag(tag_name: []const u8) bool {
|
||||
|
||||
fn getDefaultColor(element: *const Element) []const u8 {
|
||||
switch (element._type) {
|
||||
.html => |html| {
|
||||
return switch (html._type) {
|
||||
.html => {
|
||||
return switch (element.subtype(Element.Html)._type) {
|
||||
.anchor => "rgb(0, 0, 238)", // blue
|
||||
else => "rgb(0, 0, 0)",
|
||||
};
|
||||
|
||||
@@ -130,87 +130,169 @@ pub fn upgradeConstruct(frame: *Frame) !*Element {
|
||||
return node.is(Element) orelse return error.TypeError;
|
||||
}
|
||||
|
||||
pub const Type = union(enum) {
|
||||
anchor: *Anchor,
|
||||
area: *Area,
|
||||
base: *Base,
|
||||
body: *Body,
|
||||
br: *BR,
|
||||
button: *Button,
|
||||
canvas: *Canvas,
|
||||
custom: *Custom,
|
||||
data: *Data,
|
||||
datalist: *DataList,
|
||||
details: *Details,
|
||||
dialog: *Dialog,
|
||||
directory: *Directory,
|
||||
div: *Div,
|
||||
dl: *DList,
|
||||
embed: *Embed,
|
||||
fieldset: *FieldSet,
|
||||
font: *Font,
|
||||
form: *Form,
|
||||
frameset: *FrameSet,
|
||||
generic: *Generic,
|
||||
heading: *Heading,
|
||||
head: *Head,
|
||||
html: *Html,
|
||||
hr: *HR,
|
||||
img: *Image,
|
||||
iframe: *IFrame,
|
||||
input: *Input,
|
||||
label: *Label,
|
||||
legend: *Legend,
|
||||
li: *LI,
|
||||
link: *Link,
|
||||
map: *Map,
|
||||
marquee: *Marquee,
|
||||
media: *Media,
|
||||
meta: *Meta,
|
||||
meter: *Meter,
|
||||
mod: *Mod,
|
||||
object: *Object,
|
||||
ol: *OL,
|
||||
optgroup: *OptGroup,
|
||||
option: *Option,
|
||||
output: *Output,
|
||||
p: *Paragraph,
|
||||
picture: *Picture,
|
||||
param: *Param,
|
||||
pre: *Pre,
|
||||
progress: *Progress,
|
||||
quote: *Quote,
|
||||
script: *Script,
|
||||
select: *Select,
|
||||
slot: *Slot,
|
||||
source: *Source,
|
||||
span: *Span,
|
||||
style: *Style,
|
||||
table: *Table,
|
||||
table_caption: *TableCaption,
|
||||
table_cell: *TableCell,
|
||||
table_col: *TableCol,
|
||||
table_row: *TableRow,
|
||||
table_section: *TableSection,
|
||||
template: *Template,
|
||||
textarea: *TextArea,
|
||||
time: *Time,
|
||||
title: *Title,
|
||||
track: *Track,
|
||||
ul: *UL,
|
||||
unknown: *Unknown,
|
||||
pub const Type = enum(u8) {
|
||||
anchor,
|
||||
area,
|
||||
base,
|
||||
body,
|
||||
br,
|
||||
button,
|
||||
canvas,
|
||||
custom,
|
||||
data,
|
||||
datalist,
|
||||
details,
|
||||
dialog,
|
||||
directory,
|
||||
div,
|
||||
dl,
|
||||
embed,
|
||||
fieldset,
|
||||
font,
|
||||
form,
|
||||
frameset,
|
||||
generic,
|
||||
heading,
|
||||
head,
|
||||
html,
|
||||
hr,
|
||||
img,
|
||||
iframe,
|
||||
input,
|
||||
label,
|
||||
legend,
|
||||
li,
|
||||
link,
|
||||
map,
|
||||
marquee,
|
||||
media,
|
||||
meta,
|
||||
meter,
|
||||
mod,
|
||||
object,
|
||||
ol,
|
||||
optgroup,
|
||||
option,
|
||||
output,
|
||||
p,
|
||||
picture,
|
||||
param,
|
||||
pre,
|
||||
progress,
|
||||
quote,
|
||||
script,
|
||||
select,
|
||||
slot,
|
||||
source,
|
||||
span,
|
||||
style,
|
||||
table,
|
||||
table_caption,
|
||||
table_cell,
|
||||
table_col,
|
||||
table_row,
|
||||
table_section,
|
||||
template,
|
||||
textarea,
|
||||
time,
|
||||
title,
|
||||
track,
|
||||
ul,
|
||||
unknown,
|
||||
};
|
||||
|
||||
pub fn Subtype(comptime tag: Type) type {
|
||||
return switch (tag) {
|
||||
.anchor => Anchor,
|
||||
.area => Area,
|
||||
.base => Base,
|
||||
.body => Body,
|
||||
.br => BR,
|
||||
.button => Button,
|
||||
.canvas => Canvas,
|
||||
.custom => Custom,
|
||||
.data => Data,
|
||||
.datalist => DataList,
|
||||
.details => Details,
|
||||
.dialog => Dialog,
|
||||
.directory => Directory,
|
||||
.div => Div,
|
||||
.dl => DList,
|
||||
.embed => Embed,
|
||||
.fieldset => FieldSet,
|
||||
.font => Font,
|
||||
.form => Form,
|
||||
.frameset => FrameSet,
|
||||
.generic => Generic,
|
||||
.heading => Heading,
|
||||
.head => Head,
|
||||
.html => Html,
|
||||
.hr => HR,
|
||||
.img => Image,
|
||||
.iframe => IFrame,
|
||||
.input => Input,
|
||||
.label => Label,
|
||||
.legend => Legend,
|
||||
.li => LI,
|
||||
.link => Link,
|
||||
.map => Map,
|
||||
.marquee => Marquee,
|
||||
.media => Media,
|
||||
.meta => Meta,
|
||||
.meter => Meter,
|
||||
.mod => Mod,
|
||||
.object => Object,
|
||||
.ol => OL,
|
||||
.optgroup => OptGroup,
|
||||
.option => Option,
|
||||
.output => Output,
|
||||
.p => Paragraph,
|
||||
.picture => Picture,
|
||||
.param => Param,
|
||||
.pre => Pre,
|
||||
.progress => Progress,
|
||||
.quote => Quote,
|
||||
.script => Script,
|
||||
.select => Select,
|
||||
.slot => Slot,
|
||||
.source => Source,
|
||||
.span => Span,
|
||||
.style => Style,
|
||||
.table => Table,
|
||||
.table_caption => TableCaption,
|
||||
.table_cell => TableCell,
|
||||
.table_col => TableCol,
|
||||
.table_row => TableRow,
|
||||
.table_section => TableSection,
|
||||
.template => Template,
|
||||
.textarea => TextArea,
|
||||
.time => Time,
|
||||
.title => Title,
|
||||
.track => Track,
|
||||
.ul => UL,
|
||||
.unknown => Unknown,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn subtype(self: *const HtmlElement, comptime T: type) *T {
|
||||
const offset = comptime Factory.chainOffsetOf(T, T) - Factory.chainOffsetOf(T, HtmlElement);
|
||||
const sub: *T = @ptrFromInt(@intFromPtr(self) + offset);
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
// This pointer dance only works because the factory allocates the chain
|
||||
// in a contiguous block of memory. In debug, we assert this holds via
|
||||
// the _proto_canary back pointer.
|
||||
std.debug.assert(Factory.protoOf(sub) == self);
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
pub fn is(self: *HtmlElement, comptime T: type) ?*T {
|
||||
inline for (@typeInfo(Type).@"union".fields) |f| {
|
||||
if (@field(Type, f.name) == self._type) {
|
||||
if (f.type == T) {
|
||||
return &@field(self._type, f.name);
|
||||
switch (self._type) {
|
||||
inline else => |tag| {
|
||||
if (Subtype(tag) == T) {
|
||||
return self.subtype(T);
|
||||
}
|
||||
if (f.type == *T) {
|
||||
return @field(self._type, f.name);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -304,8 +386,8 @@ pub fn insertAdjacentHTML(
|
||||
|
||||
pub fn click(self: *HtmlElement, frame: *Frame) !void {
|
||||
switch (self._type) {
|
||||
inline .button, .input, .textarea, .select => |i| {
|
||||
if (i.getDisabled()) {
|
||||
inline .button, .input, .textarea, .select => |tag| {
|
||||
if (self.subtype(Subtype(tag)).getDisabled()) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
@@ -330,8 +412,8 @@ pub fn click(self: *HtmlElement, frame: *Frame) !void {
|
||||
if (event._prevent_default == false) {
|
||||
// toggle the popover_target
|
||||
const explicit: ?*Element = switch (self._type) {
|
||||
.button => |b| b._popover_target,
|
||||
.input => |i| i._popover_target,
|
||||
.button => self.subtype(Button)._popover_target,
|
||||
.input => self.subtype(Input)._popover_target,
|
||||
else => null,
|
||||
};
|
||||
try popover.runInvokerActivation(self, explicit, frame);
|
||||
@@ -543,7 +625,7 @@ fn setAttributeListener(
|
||||
) !void {
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
log.debug(.event, "Html.setAttributeListener", .{
|
||||
.type = std.meta.activeTag(self._type),
|
||||
.type = self._type,
|
||||
.listener_type = listener_type,
|
||||
});
|
||||
}
|
||||
@@ -1492,14 +1574,14 @@ fn collectInnerText(self: *HtmlElement, state: *InnerTextState) std.Io.Writer.Er
|
||||
const e = child.subtype(Node.Element);
|
||||
switch (e._type) {
|
||||
.svg => {},
|
||||
.html => |he| {
|
||||
.html => {
|
||||
const tag = e.getTag();
|
||||
switch (child_filter) {
|
||||
.none => {},
|
||||
.select => if (tag != .option and tag != .optgroup) continue,
|
||||
.optgroup => if (tag != .option) continue,
|
||||
}
|
||||
try handleChildElement(he, tag, state, &saw_cell, &saw_row);
|
||||
try handleChildElement(e.subtype(HtmlElement), tag, state, &saw_cell, &saw_row);
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -1870,17 +1952,17 @@ pub const Build = struct {
|
||||
// Calls `func_name` with `args` on the most specific type where it is
|
||||
// implement. This could be on the HtmlElement itself.
|
||||
pub fn call(self: *const HtmlElement, comptime func_name: []const u8, args: anytype) !bool {
|
||||
inline for (@typeInfo(HtmlElement.Type).@"union".fields) |f| {
|
||||
if (@field(HtmlElement.Type, f.name) == self._type) {
|
||||
switch (self._type) {
|
||||
inline else => |tag| {
|
||||
const S = Subtype(tag);
|
||||
// The inner type implements this function. Call it and we're done.
|
||||
const S = reflect.Struct(f.type);
|
||||
if (@hasDecl(S, "Build")) {
|
||||
if (@hasDecl(S.Build, func_name)) {
|
||||
try @call(.auto, @field(S.Build, func_name), args);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if (@hasDecl(HtmlElement.Build, func_name)) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// 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 lp = @import("lightpanda");
|
||||
|
||||
const js = @import("../../js/js.zig");
|
||||
@@ -47,64 +48,99 @@ _type: Type,
|
||||
_tag_name: String, // Svg elements are case-preserving
|
||||
_proto_canary: if (lp.IS_DEBUG) *Element else void = undefined,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
graphics: *Graphics,
|
||||
view: *View,
|
||||
title: *Title,
|
||||
desc: *Desc,
|
||||
metadata: *Metadata,
|
||||
gradient: *GradientElement,
|
||||
clip_path: *ClipPath,
|
||||
marker: *Marker,
|
||||
mask: *Mask,
|
||||
pattern: *Pattern,
|
||||
stop: *Stop,
|
||||
generic: *Generic,
|
||||
pub const Type = enum(u8) {
|
||||
graphics,
|
||||
view,
|
||||
title,
|
||||
desc,
|
||||
metadata,
|
||||
gradient,
|
||||
clip_path,
|
||||
marker,
|
||||
mask,
|
||||
pattern,
|
||||
stop,
|
||||
generic,
|
||||
};
|
||||
|
||||
pub fn Subtype(comptime tag: Type) type {
|
||||
return switch (tag) {
|
||||
.graphics => Graphics,
|
||||
.view => View,
|
||||
.title => Title,
|
||||
.desc => Desc,
|
||||
.metadata => Metadata,
|
||||
.gradient => GradientElement,
|
||||
.clip_path => ClipPath,
|
||||
.marker => Marker,
|
||||
.mask => Mask,
|
||||
.pattern => Pattern,
|
||||
.stop => Stop,
|
||||
.generic => Generic,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn subtype(self: *const Svg, comptime T: type) *T {
|
||||
const offset = comptime Factory.chainOffsetOf(T, T) - Factory.chainOffsetOf(T, Svg);
|
||||
const sub: *T = @ptrFromInt(@intFromPtr(self) + offset);
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
// This pointer dance only works because the factory allocates the chain
|
||||
// in a contiguous block of memory. In debug, we assert this holds via
|
||||
// the _proto_canary back pointer.
|
||||
std.debug.assert(Factory.protoOf(sub) == self);
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
pub fn is(self: *Svg, comptime T: type) ?*T {
|
||||
inline for (@typeInfo(Type).@"union".fields) |field| {
|
||||
if (@field(Type, field.name) == self._type) {
|
||||
if (field.type == *T) {
|
||||
return @field(self._type, field.name);
|
||||
switch (self._type) {
|
||||
inline else => |tag| {
|
||||
if (Subtype(tag) == T) {
|
||||
return self.subtype(T);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
if (self._type == .graphics) {
|
||||
return self._type.graphics.is(T);
|
||||
return self.subtype(Graphics).is(T);
|
||||
}
|
||||
if (self._type == .gradient) {
|
||||
return self._type.gradient.is(T);
|
||||
return self.subtype(GradientElement).is(T);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getTag(self: *const Svg) Element.Tag {
|
||||
return switch (self._type) {
|
||||
.graphics => |g| switch (g._type) {
|
||||
.svg => .svg,
|
||||
.g => .g,
|
||||
// No dedicated Element.Tag values; tag-name matching falls back
|
||||
// to _tag_name, like it does for generic SVG elements.
|
||||
.a, .use, .image, .defs, .symbol, .switch_element, .foreign_object => .unknown,
|
||||
.text_content => |content| switch (content._type) {
|
||||
.positioning => |positioning| switch (positioning._type) {
|
||||
.text => .text,
|
||||
.tspan => .unknown,
|
||||
.graphics => blk: {
|
||||
const g = self.subtype(Graphics);
|
||||
break :blk switch (g._type) {
|
||||
.svg => .svg,
|
||||
.g => .g,
|
||||
// No dedicated Element.Tag values; tag-name matching falls back
|
||||
// to _tag_name, like it does for generic SVG elements.
|
||||
.a, .use, .image, .defs, .symbol, .switch_element, .foreign_object => .unknown,
|
||||
.text_content => tc: {
|
||||
const content = g.subtype(Graphics.TextContent);
|
||||
break :tc switch (content._type) {
|
||||
.positioning => switch (content.subtype(Graphics.TextContent.TextPositioning)._type) {
|
||||
.text => .text,
|
||||
.tspan => .unknown,
|
||||
},
|
||||
.text_path => .unknown,
|
||||
};
|
||||
},
|
||||
.text_path => .unknown,
|
||||
},
|
||||
.geometry => |geo| switch (geo._type) {
|
||||
.rect => .rect,
|
||||
.circle => .circle,
|
||||
.ellipse => .ellipse,
|
||||
.line => .line,
|
||||
.path => .path,
|
||||
.polygon => .polygon,
|
||||
.polyline => .polyline,
|
||||
},
|
||||
.geometry => switch (g.subtype(Graphics.Geometry)._type) {
|
||||
.rect => .rect,
|
||||
.circle => .circle,
|
||||
.ellipse => .ellipse,
|
||||
.line => .line,
|
||||
.path => .path,
|
||||
.polygon => .polygon,
|
||||
.polyline => .polyline,
|
||||
},
|
||||
};
|
||||
},
|
||||
.generic => |g| g._tag,
|
||||
.generic => self.subtype(Generic)._tag,
|
||||
.title => .title,
|
||||
.view, .desc, .metadata, .gradient, .clip_path, .marker, .mask, .pattern, .stop => .unknown,
|
||||
};
|
||||
|
||||
@@ -49,7 +49,7 @@ fn isLabelable(el: *Element) bool {
|
||||
const html = el.is(HtmlElement) orelse return false;
|
||||
return switch (html._type) {
|
||||
.button, .meter, .output, .progress, .select, .textarea => true,
|
||||
.input => |input| input._input_type != .hidden,
|
||||
.input => html.subtype(HtmlElement.Input)._input_type != .hidden,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,12 +49,34 @@ pub const NetworkState = enum(u16) {
|
||||
NETWORK_NO_SOURCE = 3,
|
||||
};
|
||||
|
||||
pub const Type = union(enum) {
|
||||
pub const Type = enum(u8) {
|
||||
generic,
|
||||
audio: *Audio,
|
||||
video: *Video,
|
||||
audio,
|
||||
video,
|
||||
};
|
||||
|
||||
// `.generic` maps to Media itself: a bare <media> chain ends at Media, so the
|
||||
// tag has no chain member of its own.
|
||||
pub fn Subtype(comptime tag: Type) type {
|
||||
return switch (tag) {
|
||||
.generic => Media,
|
||||
.audio => Audio,
|
||||
.video => Video,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn subtype(self: *const Media, comptime T: type) *T {
|
||||
const offset = comptime Factory.chainOffsetOf(T, T) - Factory.chainOffsetOf(T, Media);
|
||||
const sub: *T = @ptrFromInt(@intFromPtr(self) + offset);
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
// This pointer dance only works because the factory allocates the chain
|
||||
// in a contiguous block of memory. In debug, we assert this holds via
|
||||
// the _proto_canary back pointer.
|
||||
std.debug.assert(Factory.protoOf(sub) == self);
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
_type: Type,
|
||||
_proto_canary: if (lp.IS_DEBUG) *HtmlElement else void = undefined,
|
||||
_paused: bool = true,
|
||||
@@ -77,18 +99,10 @@ pub fn asNode(self: *Media) *Node {
|
||||
}
|
||||
|
||||
pub fn is(self: *Media, comptime T: type) ?*T {
|
||||
const type_name = @typeName(T);
|
||||
switch (self._type) {
|
||||
.audio => |a| {
|
||||
if (T == *Audio) return a;
|
||||
if (comptime std.mem.startsWith(u8, type_name, "browser.webapi.element.html.Audio")) {
|
||||
return a;
|
||||
}
|
||||
},
|
||||
.video => |v| {
|
||||
if (T == *Video) return v;
|
||||
if (comptime std.mem.startsWith(u8, type_name, "browser.webapi.element.html.Video")) {
|
||||
return v;
|
||||
inline .audio, .video => |tag| {
|
||||
if (Subtype(tag) == T) {
|
||||
return self.subtype(T);
|
||||
}
|
||||
},
|
||||
.generic => {},
|
||||
|
||||
@@ -207,7 +207,7 @@ pub fn invokerTarget(invoker: *Node, explicit: ?*Element, frame: *Frame) ?*Eleme
|
||||
pub fn runInvokerActivation(invoker: *HtmlElement, explicit: ?*Element, frame: *Frame) !void {
|
||||
switch (invoker._type) {
|
||||
.button => {},
|
||||
.input => |input| switch (input._input_type) {
|
||||
.input => switch (invoker.subtype(HtmlElement.Input)._input_type) {
|
||||
.button, .submit, .reset, .image => {},
|
||||
else => return, // not an invoker
|
||||
},
|
||||
|
||||
@@ -45,23 +45,47 @@ pub const Proto = Graphics;
|
||||
_type: Type,
|
||||
_proto_canary: if (lp.IS_DEBUG) *Graphics else void = undefined,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
rect: *Rect,
|
||||
circle: *Circle,
|
||||
ellipse: *Ellipse,
|
||||
line: *Line,
|
||||
path: *Path,
|
||||
polygon: *Polygon,
|
||||
polyline: *Polyline,
|
||||
pub const Type = enum(u8) {
|
||||
rect,
|
||||
circle,
|
||||
ellipse,
|
||||
line,
|
||||
path,
|
||||
polygon,
|
||||
polyline,
|
||||
};
|
||||
|
||||
pub fn Subtype(comptime tag: Type) type {
|
||||
return switch (tag) {
|
||||
.rect => Rect,
|
||||
.circle => Circle,
|
||||
.ellipse => Ellipse,
|
||||
.line => Line,
|
||||
.path => Path,
|
||||
.polygon => Polygon,
|
||||
.polyline => Polyline,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn subtype(self: *const Geometry, comptime T: type) *T {
|
||||
const offset = comptime Factory.chainOffsetOf(T, T) - Factory.chainOffsetOf(T, Geometry);
|
||||
const sub: *T = @ptrFromInt(@intFromPtr(self) + offset);
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
// This pointer dance only works because the factory allocates the chain
|
||||
// in a contiguous block of memory. In debug, we assert this holds via
|
||||
// the _proto_canary back pointer.
|
||||
std.debug.assert(Factory.protoOf(sub) == self);
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
pub fn is(self: *Geometry, comptime T: type) ?*T {
|
||||
inline for (@typeInfo(Type).@"union".fields) |f| {
|
||||
if (@field(Type, f.name) == self._type) {
|
||||
if (f.type == *T) {
|
||||
return @field(self._type, f.name);
|
||||
switch (self._type) {
|
||||
inline else => |tag| {
|
||||
if (Subtype(tag) == T) {
|
||||
return self.subtype(T);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -107,16 +131,16 @@ pub fn getPointAtLength(self: *Geometry, distance: f64, frame: *Frame) !*DOMPoin
|
||||
|
||||
pub fn buildPath(self: *Geometry, frame: *Frame) !PathData.Path {
|
||||
return switch (self._type) {
|
||||
.rect => |rect| buildRect(rect, frame),
|
||||
.circle => |circle| buildCircle(circle, frame),
|
||||
.ellipse => |ellipse| buildEllipse(ellipse, frame),
|
||||
.line => |line| buildLine(line, frame),
|
||||
.path => |path| PathData.parse(
|
||||
path.asElement().getAttributeSafe(comptime .wrap("d")) orelse "",
|
||||
.rect => buildRect(self.subtype(Rect), frame),
|
||||
.circle => buildCircle(self.subtype(Circle), frame),
|
||||
.ellipse => buildEllipse(self.subtype(Ellipse), frame),
|
||||
.line => buildLine(self.subtype(Line), frame),
|
||||
.path => PathData.parse(
|
||||
self.subtype(Path).asElement().getAttributeSafe(comptime .wrap("d")) orelse "",
|
||||
frame.local_arena,
|
||||
),
|
||||
.polygon => |polygon| buildPoints(try polygon.getPoints(frame), true, frame),
|
||||
.polyline => |polyline| buildPoints(try polyline.getPoints(frame), false, frame),
|
||||
.polygon => buildPoints(try self.subtype(Polygon).getPoints(frame), true, frame),
|
||||
.polyline => buildPoints(try self.subtype(Polyline).getPoints(frame), false, frame),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// 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 lp = @import("lightpanda");
|
||||
const js = @import("../../../js/js.zig");
|
||||
const Frame = @import("../../../Frame.zig");
|
||||
@@ -39,18 +40,37 @@ pub const Proto = Svg;
|
||||
_type: Type,
|
||||
_proto_canary: if (lp.IS_DEBUG) *Svg else void = undefined,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
linear: *LinearGradient,
|
||||
radial: *RadialGradient,
|
||||
pub const Type = enum(u8) {
|
||||
linear,
|
||||
radial,
|
||||
};
|
||||
|
||||
pub fn Subtype(comptime tag: Type) type {
|
||||
return switch (tag) {
|
||||
.linear => LinearGradient,
|
||||
.radial => RadialGradient,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn subtype(self: *const GradientElement, comptime T: type) *T {
|
||||
const offset = comptime Factory.chainOffsetOf(T, T) - Factory.chainOffsetOf(T, GradientElement);
|
||||
const sub: *T = @ptrFromInt(@intFromPtr(self) + offset);
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
// This pointer dance only works because the factory allocates the chain
|
||||
// in a contiguous block of memory. In debug, we assert this holds via
|
||||
// the _proto_canary back pointer.
|
||||
std.debug.assert(Factory.protoOf(sub) == self);
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
pub fn is(self: *GradientElement, comptime T: type) ?*T {
|
||||
inline for (@typeInfo(Type).@"union".fields) |field| {
|
||||
if (@field(Type, field.name) == self._type) {
|
||||
if (field.type == *T) {
|
||||
return @field(self._type, field.name);
|
||||
switch (self._type) {
|
||||
inline else => |tag| {
|
||||
if (Subtype(tag) == T) {
|
||||
return self.subtype(T);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -49,33 +49,61 @@ pub const Proto = SvgElement;
|
||||
_type: Type,
|
||||
_proto_canary: if (lp.IS_DEBUG) *SvgElement else void = undefined,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
svg: *Svg,
|
||||
g: *G,
|
||||
a: *A,
|
||||
use: *Use,
|
||||
image: *Image,
|
||||
defs: *Defs,
|
||||
symbol: *Symbol,
|
||||
switch_element: *Switch,
|
||||
foreign_object: *ForeignObject,
|
||||
text_content: *TextContent,
|
||||
geometry: *Geometry,
|
||||
pub const Type = enum(u8) {
|
||||
svg,
|
||||
g,
|
||||
a,
|
||||
use,
|
||||
image,
|
||||
defs,
|
||||
symbol,
|
||||
switch_element,
|
||||
foreign_object,
|
||||
text_content,
|
||||
geometry,
|
||||
};
|
||||
|
||||
pub fn Subtype(comptime tag: Type) type {
|
||||
return switch (tag) {
|
||||
.svg => Svg,
|
||||
.g => G,
|
||||
.a => A,
|
||||
.use => Use,
|
||||
.image => Image,
|
||||
.defs => Defs,
|
||||
.symbol => Symbol,
|
||||
.switch_element => Switch,
|
||||
.foreign_object => ForeignObject,
|
||||
.text_content => TextContent,
|
||||
.geometry => Geometry,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn subtype(self: *const Graphics, comptime T: type) *T {
|
||||
const offset = comptime Factory.chainOffsetOf(T, T) - Factory.chainOffsetOf(T, Graphics);
|
||||
const sub: *T = @ptrFromInt(@intFromPtr(self) + offset);
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
// This pointer dance only works because the factory allocates the chain
|
||||
// in a contiguous block of memory. In debug, we assert this holds via
|
||||
// the _proto_canary back pointer.
|
||||
std.debug.assert(Factory.protoOf(sub) == self);
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
pub fn is(self: *Graphics, comptime T: type) ?*T {
|
||||
inline for (@typeInfo(Type).@"union".fields) |f| {
|
||||
if (@field(Type, f.name) == self._type) {
|
||||
if (f.type == *T) {
|
||||
return @field(self._type, f.name);
|
||||
switch (self._type) {
|
||||
inline else => |tag| {
|
||||
if (Subtype(tag) == T) {
|
||||
return self.subtype(T);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
if (self._type == .geometry) {
|
||||
return self._type.geometry.is(T);
|
||||
return self.subtype(Geometry).is(T);
|
||||
}
|
||||
if (self._type == .text_content) {
|
||||
return self._type.text_content.is(T);
|
||||
return self.subtype(TextContent).is(T);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -108,12 +136,12 @@ pub const JsApi = struct {
|
||||
pub fn getBBox(self: *Graphics, frame: *Frame) !*DOMRect {
|
||||
var bounds: PathData.Bounds = .{};
|
||||
switch (self._type) {
|
||||
.geometry => |geometry| {
|
||||
var path = try geometry.buildPath(frame);
|
||||
.geometry => {
|
||||
var path = try self.subtype(Geometry).buildPath(frame);
|
||||
defer path.deinit(frame.local_arena);
|
||||
bounds = path.bounds(.{});
|
||||
},
|
||||
.foreign_object => |foreign_object| bounds = try foreign_object.getBounds(frame),
|
||||
.foreign_object => bounds = try self.subtype(ForeignObject).getBounds(frame),
|
||||
.g, .a, .svg => try accumulateChildren(self, .{}, &bounds, frame),
|
||||
.defs, .symbol, .switch_element, .use, .image, .text_content => {},
|
||||
}
|
||||
@@ -156,13 +184,13 @@ fn accumulateChildren(parent: *Graphics, matrix: PathData.Matrix, bounds: *PathD
|
||||
const child_matrix = parent_matrix.multiply(transformMatrix(element));
|
||||
|
||||
switch (graphics._type) {
|
||||
.geometry => |geometry| {
|
||||
var path = try geometry.buildPath(frame);
|
||||
.geometry => {
|
||||
var path = try graphics.subtype(Geometry).buildPath(frame);
|
||||
defer path.deinit(frame.local_arena);
|
||||
bounds.merge(path.bounds(child_matrix));
|
||||
},
|
||||
.foreign_object => |foreign_object| {
|
||||
const child_bounds = try foreign_object.getBounds(frame);
|
||||
.foreign_object => {
|
||||
const child_bounds = try graphics.subtype(ForeignObject).getBounds(frame);
|
||||
if (!child_bounds.isEmpty()) {
|
||||
var foreign_path: PathData.Path = .{};
|
||||
defer foreign_path.deinit(frame.local_arena);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// 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 lp = @import("lightpanda");
|
||||
const js = @import("../../../js/js.zig");
|
||||
const Frame = @import("../../../Frame.zig");
|
||||
@@ -39,18 +40,39 @@ pub const Proto = Graphics;
|
||||
_type: Type,
|
||||
_proto_canary: if (lp.IS_DEBUG) *Graphics else void = undefined,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
positioning: *TextPositioning,
|
||||
text_path: *TextPath,
|
||||
pub const Type = enum(u8) {
|
||||
positioning,
|
||||
text_path,
|
||||
};
|
||||
|
||||
pub fn is(self: *TextContent, comptime T: type) ?*T {
|
||||
inline for (@typeInfo(Type).@"union".fields) |field| {
|
||||
if (@field(Type, field.name) == self._type) {
|
||||
if (field.type == *T) return @field(self._type, field.name);
|
||||
}
|
||||
pub fn Subtype(comptime tag: Type) type {
|
||||
return switch (tag) {
|
||||
.positioning => TextPositioning,
|
||||
.text_path => TextPath,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn subtype(self: *const TextContent, comptime T: type) *T {
|
||||
const offset = comptime Factory.chainOffsetOf(T, T) - Factory.chainOffsetOf(T, TextContent);
|
||||
const sub: *T = @ptrFromInt(@intFromPtr(self) + offset);
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
// This pointer dance only works because the factory allocates the chain
|
||||
// in a contiguous block of memory. In debug, we assert this holds via
|
||||
// the _proto_canary back pointer.
|
||||
std.debug.assert(Factory.protoOf(sub) == self);
|
||||
}
|
||||
if (self._type == .positioning) return self._type.positioning.is(T);
|
||||
return sub;
|
||||
}
|
||||
|
||||
pub fn is(self: *TextContent, comptime T: type) ?*T {
|
||||
switch (self._type) {
|
||||
inline else => |tag| {
|
||||
if (Subtype(tag) == T) {
|
||||
return self.subtype(T);
|
||||
}
|
||||
},
|
||||
}
|
||||
if (self._type == .positioning) return self.subtype(TextPositioning).is(T);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
// 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 lp = @import("lightpanda");
|
||||
const js = @import("../../../js/js.zig");
|
||||
|
||||
@@ -34,16 +35,37 @@ pub const Proto = TextContent;
|
||||
_type: Type,
|
||||
_proto_canary: if (lp.IS_DEBUG) *TextContent else void = undefined,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
text: *Text,
|
||||
tspan: *TSpan,
|
||||
pub const Type = enum(u8) {
|
||||
text,
|
||||
tspan,
|
||||
};
|
||||
|
||||
pub fn Subtype(comptime tag: Type) type {
|
||||
return switch (tag) {
|
||||
.text => Text,
|
||||
.tspan => TSpan,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn subtype(self: *const TextPositioning, comptime T: type) *T {
|
||||
const offset = comptime Factory.chainOffsetOf(T, T) - Factory.chainOffsetOf(T, TextPositioning);
|
||||
const sub: *T = @ptrFromInt(@intFromPtr(self) + offset);
|
||||
if (comptime lp.IS_DEBUG) {
|
||||
// This pointer dance only works because the factory allocates the chain
|
||||
// in a contiguous block of memory. In debug, we assert this holds via
|
||||
// the _proto_canary back pointer.
|
||||
std.debug.assert(Factory.protoOf(sub) == self);
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
|
||||
pub fn is(self: *TextPositioning, comptime T: type) ?*T {
|
||||
inline for (@typeInfo(Type).@"union".fields) |field| {
|
||||
if (@field(Type, field.name) == self._type) {
|
||||
if (field.type == *T) return @field(self._type, field.name);
|
||||
}
|
||||
switch (self._type) {
|
||||
inline else => |tag| {
|
||||
if (Subtype(tag) == T) {
|
||||
return self.subtype(T);
|
||||
}
|
||||
},
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user