mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
Various HTML attribute tweaks
Aimed at improving WPT /html/dom/reflection-obsolete.html Goes from 923 to 2305 passing cases (the remaining failing cases are all for <frame> which we don't currently support) Add accessors to Directory, Font and FrameSet. Add HTMLMarqueeElement. Font setColor null -> "" Add new properties to Html (accessKey and autofocus) and improve tabIndex parsing.
This commit is contained in:
@@ -2586,10 +2586,10 @@ pub fn createElementNS(self: *Frame, namespace: Element.Namespace, name: []const
|
||||
.{ ._proto = undefined },
|
||||
),
|
||||
asUint("marquee") => return self.createHtmlElementT(
|
||||
Element.Html.Generic,
|
||||
Element.Html.Marquee,
|
||||
namespace,
|
||||
attribute_iterator,
|
||||
.{ ._proto = undefined, ._tag_name = comptime .wrap("marquee"), ._tag = .marquee },
|
||||
.{ ._proto = undefined },
|
||||
),
|
||||
asUint("address") => return self.createHtmlElementT(
|
||||
Element.Html.Generic,
|
||||
|
||||
@@ -866,6 +866,7 @@ pub const PageJsApis = flattenTypes(&.{
|
||||
@import("../webapi/element/html/LI.zig"),
|
||||
@import("../webapi/element/html/Link.zig"),
|
||||
@import("../webapi/element/html/Map.zig"),
|
||||
@import("../webapi/element/html/Marquee.zig"),
|
||||
@import("../webapi/element/html/Media.zig"),
|
||||
@import("../webapi/element/html/Meta.zig"),
|
||||
@import("../webapi/element/html/Meter.zig"),
|
||||
|
||||
47
src/browser/tests/element/html/font.html
Normal file
47
src/browser/tests/element/html/font.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../../testing.js"></script>
|
||||
|
||||
<script id="font_constructor">
|
||||
{
|
||||
testing.expectEqual("function", typeof HTMLFontElement)
|
||||
|
||||
const f = document.createElement('font')
|
||||
testing.expectEqual("[object HTMLFontElement]", f.toString())
|
||||
testing.expectEqual("FONT", f.tagName)
|
||||
testing.expectEqual(true, f instanceof HTMLFontElement)
|
||||
testing.expectEqual(true, f instanceof HTMLElement)
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="font_string_attributes">
|
||||
{
|
||||
const f = document.createElement('font')
|
||||
|
||||
testing.expectEqual("", f.color)
|
||||
testing.expectEqual("", f.face)
|
||||
testing.expectEqual("", f.size)
|
||||
|
||||
f.color = "red"
|
||||
f.face = "serif"
|
||||
f.size = "7"
|
||||
testing.expectEqual("red", f.color)
|
||||
testing.expectEqual("serif", f.face)
|
||||
testing.expectEqual("7", f.size)
|
||||
testing.expectEqual("red", f.getAttribute("color"))
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="font_color_legacy_null_to_empty_string">
|
||||
{
|
||||
// color is `[LegacyNullToEmptyString] DOMString`: null coerces to "", unlike
|
||||
// face/size which stringify null to "null".
|
||||
const f = document.createElement('font')
|
||||
|
||||
f.color = null
|
||||
testing.expectEqual("", f.color)
|
||||
testing.expectEqual("", f.getAttribute("color"))
|
||||
|
||||
f.face = null
|
||||
testing.expectEqual("null", f.face)
|
||||
}
|
||||
</script>
|
||||
@@ -52,5 +52,33 @@
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
testing.expectEqual(0, textarea.tabIndex);
|
||||
|
||||
// tabIndex follows the HTML "rules for parsing integers": skip leading
|
||||
// ASCII whitespace, take an optional sign and the leading run of digits,
|
||||
// ignoring any trailing junk.
|
||||
d3.setAttribute('tabindex', ' 7');
|
||||
testing.expectEqual(7, d3.tabIndex);
|
||||
|
||||
d3.setAttribute('tabindex', '\t\n\f\r9');
|
||||
testing.expectEqual(9, d3.tabIndex);
|
||||
|
||||
d3.setAttribute('tabindex', '7%');
|
||||
testing.expectEqual(7, d3.tabIndex);
|
||||
|
||||
d3.setAttribute('tabindex', '1.5');
|
||||
testing.expectEqual(1, d3.tabIndex);
|
||||
|
||||
d3.setAttribute('tabindex', '-3');
|
||||
testing.expectEqual(-3, d3.tabIndex);
|
||||
|
||||
d3.setAttribute('tabindex', '+4');
|
||||
testing.expectEqual(4, d3.tabIndex);
|
||||
|
||||
// Non-numeric values fall back to the element's default (-1 here).
|
||||
d3.setAttribute('tabindex', 'foo');
|
||||
testing.expectEqual(-1, d3.tabIndex);
|
||||
|
||||
d3.setAttribute('tabindex', '');
|
||||
testing.expectEqual(-1, d3.tabIndex);
|
||||
}
|
||||
</script>
|
||||
|
||||
100
src/browser/tests/element/html/marquee.html
Normal file
100
src/browser/tests/element/html/marquee.html
Normal file
@@ -0,0 +1,100 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../../testing.js"></script>
|
||||
|
||||
<script id="marquee_constructor">
|
||||
{
|
||||
testing.expectEqual("function", typeof HTMLMarqueeElement)
|
||||
|
||||
const m = document.createElement('marquee')
|
||||
testing.expectEqual("[object HTMLMarqueeElement]", m.toString())
|
||||
testing.expectEqual("MARQUEE", m.tagName)
|
||||
testing.expectEqual(true, m instanceof HTMLMarqueeElement)
|
||||
testing.expectEqual(true, m instanceof HTMLElement)
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="marquee_string_attributes">
|
||||
{
|
||||
const m = document.createElement('marquee')
|
||||
|
||||
// Plain string reflects.
|
||||
testing.expectEqual("", m.bgColor)
|
||||
m.bgColor = "red"
|
||||
testing.expectEqual("red", m.bgColor)
|
||||
testing.expectEqual("red", m.getAttribute("bgcolor"))
|
||||
|
||||
m.height = "100"
|
||||
m.width = "200"
|
||||
testing.expectEqual("100", m.height)
|
||||
testing.expectEqual("200", m.width)
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="marquee_enumerated_attributes">
|
||||
{
|
||||
const m = document.createElement('marquee')
|
||||
|
||||
// Limited-to-known-values with a default (missing/invalid -> default).
|
||||
testing.expectEqual("scroll", m.behavior)
|
||||
testing.expectEqual("left", m.direction)
|
||||
|
||||
m.setAttribute("behavior", "ALTERNATE")
|
||||
testing.expectEqual("alternate", m.behavior)
|
||||
m.setAttribute("behavior", "nonsense")
|
||||
testing.expectEqual("scroll", m.behavior)
|
||||
|
||||
m.setAttribute("direction", "Up")
|
||||
testing.expectEqual("up", m.direction)
|
||||
m.setAttribute("direction", "sideways")
|
||||
testing.expectEqual("left", m.direction)
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="marquee_boolean_attribute">
|
||||
{
|
||||
const m = document.createElement('marquee')
|
||||
|
||||
testing.expectEqual(false, m.trueSpeed)
|
||||
m.trueSpeed = true
|
||||
testing.expectEqual(true, m.trueSpeed)
|
||||
testing.expectEqual("", m.getAttribute("truespeed"))
|
||||
m.trueSpeed = false
|
||||
testing.expectEqual(false, m.trueSpeed)
|
||||
testing.expectEqual(false, m.hasAttribute("truespeed"))
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="marquee_unsigned_long_attributes">
|
||||
{
|
||||
const m = document.createElement('marquee')
|
||||
|
||||
// Defaults when unset.
|
||||
testing.expectEqual(0, m.hspace)
|
||||
testing.expectEqual(0, m.vspace)
|
||||
testing.expectEqual(6, m.scrollAmount)
|
||||
testing.expectEqual(85, m.scrollDelay)
|
||||
|
||||
// Rules for parsing non-negative integers: ASCII whitespace prefix and
|
||||
// trailing junk are allowed; VT (\v) is not whitespace; negatives and
|
||||
// out-of-range values fall back to the default.
|
||||
m.setAttribute("hspace", " \t7px")
|
||||
testing.expectEqual(7, m.hspace)
|
||||
m.setAttribute("hspace", "\v7")
|
||||
testing.expectEqual(0, m.hspace)
|
||||
m.setAttribute("hspace", "-1")
|
||||
testing.expectEqual(0, m.hspace)
|
||||
m.setAttribute("scrollAmount", "2147483648")
|
||||
testing.expectEqual(6, m.scrollAmount)
|
||||
m.setAttribute("scrollAmount", "2147483647")
|
||||
testing.expectEqual(2147483647, m.scrollAmount)
|
||||
|
||||
// IDL setter: in-range values are written through, values above the
|
||||
// signed-long max are coerced to the default.
|
||||
m.hspace = 42
|
||||
testing.expectEqual(42, m.hspace)
|
||||
testing.expectEqual("42", m.getAttribute("hspace"))
|
||||
m.scrollAmount = 4294967295
|
||||
testing.expectEqual(6, m.scrollAmount)
|
||||
testing.expectEqual("6", m.getAttribute("scrollamount"))
|
||||
}
|
||||
</script>
|
||||
@@ -233,6 +233,7 @@ pub fn getTagNameLower(self: *const Element) []const u8 {
|
||||
.li => "li",
|
||||
.link => "link",
|
||||
.map => "map",
|
||||
.marquee => "marquee",
|
||||
.media => |m| switch (m._type) {
|
||||
.audio => "audio",
|
||||
.video => "video",
|
||||
@@ -313,6 +314,7 @@ pub fn getTagNameSpec(self: *const Element, buf: []u8) []const u8 {
|
||||
.li => "LI",
|
||||
.link => "LINK",
|
||||
.map => "MAP",
|
||||
.marquee => "MARQUEE",
|
||||
.meta => "META",
|
||||
.media => |m| switch (m._type) {
|
||||
.audio => "AUDIO",
|
||||
@@ -1507,6 +1509,7 @@ pub fn getTag(self: *const Element) Tag {
|
||||
.legend => .legend,
|
||||
.li => .li,
|
||||
.map => .map,
|
||||
.marquee => .marquee,
|
||||
.ul => .ul,
|
||||
.ol => .ol,
|
||||
.object => .object,
|
||||
|
||||
@@ -722,7 +722,7 @@ fn getDefaultDisplay(element: *const Element) []const u8 {
|
||||
.html => |html| {
|
||||
return switch (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, .meter, .object, .optgroup, .output, .param, .picture, .pre, .progress, .source, .table, .table_caption, .table_cell, .table_col, .table_row, .table_section, .track => "block",
|
||||
.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: {
|
||||
const tag = element.getTagNameLower();
|
||||
if (isInlineTag(tag)) break :blk "inline";
|
||||
|
||||
@@ -61,6 +61,7 @@ pub const Legend = @import("html/Legend.zig");
|
||||
pub const LI = @import("html/LI.zig");
|
||||
pub const Link = @import("html/Link.zig");
|
||||
pub const Map = @import("html/Map.zig");
|
||||
pub const Marquee = @import("html/Marquee.zig");
|
||||
pub const Media = @import("html/Media.zig");
|
||||
pub const Meta = @import("html/Meta.zig");
|
||||
pub const Meter = @import("html/Meter.zig");
|
||||
@@ -151,6 +152,7 @@ pub const Type = union(enum) {
|
||||
li: *LI,
|
||||
link: *Link,
|
||||
map: *Map,
|
||||
marquee: *Marquee,
|
||||
media: *Media,
|
||||
meta: *Meta,
|
||||
meter: *Meter,
|
||||
@@ -342,14 +344,50 @@ pub fn setHidden(self: *HtmlElement, hidden: bool, frame: *Frame) !void {
|
||||
}
|
||||
|
||||
pub fn getTabIndex(self: *HtmlElement) i32 {
|
||||
const attr = self.asElement().getAttributeSafe(comptime .wrap("tabindex")) orelse {
|
||||
// Per spec, interactive/focusable elements default to 0 when tabindex is absent
|
||||
return switch (self._type) {
|
||||
.anchor, .area, .button, .input, .select, .textarea, .iframe => 0,
|
||||
else => -1,
|
||||
};
|
||||
const default: i32 = switch (self._type) {
|
||||
.anchor, .area, .button, .input, .select, .textarea, .iframe => 0,
|
||||
else => -1,
|
||||
};
|
||||
return std.fmt.parseInt(i32, attr, 10) catch -1;
|
||||
const attr = self.asElement().getAttributeSafe(comptime .wrap("tabindex")) orelse return default;
|
||||
return parseInteger(attr) orelse default;
|
||||
}
|
||||
|
||||
// HTML integer parsing is lax
|
||||
pub fn parseInteger(input: []const u8) ?i32 {
|
||||
var normalized = std.mem.trimStart(u8, input, "\t\n\r\x0c ");
|
||||
if (normalized.len == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var negative = false;
|
||||
if (normalized[0] == '-') {
|
||||
negative = true;
|
||||
normalized = normalized[1..];
|
||||
} else if (normalized[0] == '+') {
|
||||
normalized = normalized[1..];
|
||||
}
|
||||
|
||||
if (normalized.len == 0 or std.ascii.isDigit(normalized[0]) == false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var i: usize = 0;
|
||||
var value: i64 = 0;
|
||||
while (i < normalized.len and std.ascii.isDigit(normalized[i])) : (i += 1) {
|
||||
value = value * 10 + (normalized[i] - '0');
|
||||
if (value > 2147483648) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (negative) {
|
||||
value = -value;
|
||||
}
|
||||
|
||||
if (value < -2147483648 or value > 2147483647) {
|
||||
return null;
|
||||
}
|
||||
return @intCast(value);
|
||||
}
|
||||
|
||||
pub fn setTabIndex(self: *HtmlElement, value: i32, frame: *Frame) !void {
|
||||
@@ -359,13 +397,41 @@ pub fn setTabIndex(self: *HtmlElement, value: i32, frame: *Frame) !void {
|
||||
}
|
||||
|
||||
pub fn getDir(self: *HtmlElement) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("dir")) orelse "";
|
||||
// `dir` reflects as a "limited to only known values" enumerated
|
||||
// attribute: the getter returns the canonical (lowercase) keyword only
|
||||
// when the content attribute matches one ASCII-case-insensitively,
|
||||
// otherwise the empty string.
|
||||
const attr = self.asElement().getAttributeSafe(comptime .wrap("dir")) orelse return "";
|
||||
inline for (.{ "ltr", "rtl", "auto" }) |keyword| {
|
||||
if (std.ascii.eqlIgnoreCase(attr, keyword)) return keyword;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
pub fn setDir(self: *HtmlElement, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("dir"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub fn getAccessKey(self: *HtmlElement) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("accesskey")) orelse "";
|
||||
}
|
||||
|
||||
pub fn setAccessKey(self: *HtmlElement, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("accesskey"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub fn getAutofocus(self: *HtmlElement) bool {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("autofocus")) != null;
|
||||
}
|
||||
|
||||
pub fn setAutofocus(self: *HtmlElement, autofocus: bool, frame: *Frame) !void {
|
||||
if (autofocus) {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("autofocus"), .wrap(""), frame);
|
||||
} else {
|
||||
try self.asElement().removeAttribute(comptime .wrap("autofocus"), frame);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getLang(self: *HtmlElement) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("lang")) orelse "";
|
||||
}
|
||||
@@ -1243,6 +1309,8 @@ pub const JsApi = struct {
|
||||
pub const insertAdjacentHTML = bridge.function(HtmlElement.insertAdjacentHTML, .{ .dom_exception = true, .ce_reactions = true });
|
||||
pub const click = bridge.function(HtmlElement.click, .{});
|
||||
|
||||
pub const accessKey = bridge.accessor(HtmlElement.getAccessKey, HtmlElement.setAccessKey, .{ .ce_reactions = true });
|
||||
pub const autofocus = bridge.accessor(HtmlElement.getAutofocus, HtmlElement.setAutofocus, .{ .ce_reactions = true });
|
||||
pub const dir = bridge.accessor(HtmlElement.getDir, HtmlElement.setDir, .{ .ce_reactions = true });
|
||||
pub const hidden = bridge.accessor(HtmlElement.getHidden, HtmlElement.setHidden, .{ .ce_reactions = true });
|
||||
pub const isContentEditable = bridge.accessor(HtmlElement.getIsContentEditable, null, .{});
|
||||
|
||||
@@ -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");
|
||||
@@ -14,6 +15,18 @@ pub fn asNode(self: *Directory) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub fn getCompact(self: *Directory) bool {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("compact")) != null;
|
||||
}
|
||||
|
||||
pub fn setCompact(self: *Directory, compact: bool, frame: *Frame) !void {
|
||||
if (compact) {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("compact"), .wrap(""), frame);
|
||||
} else {
|
||||
try self.asElement().removeAttribute(comptime .wrap("compact"), frame);
|
||||
}
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Directory);
|
||||
|
||||
@@ -22,4 +35,6 @@ pub const JsApi = struct {
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const compact = bridge.accessor(Directory.getCompact, Directory.setCompact, .{ .ce_reactions = true });
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
@@ -14,6 +15,33 @@ pub fn asNode(self: *Font) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub fn getColor(self: *Font) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("color")) orelse "";
|
||||
}
|
||||
|
||||
pub fn setColor(self: *Font, value: js.Value, frame: *Frame) !void {
|
||||
// color is `[LegacyNullToEmptyString] DOMString`: a JS null becomes "",
|
||||
// not the string "null".
|
||||
const str: []const u8 = if (value.isNull()) "" else try value.toZig([]const u8);
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("color"), .wrap(str), frame);
|
||||
}
|
||||
|
||||
pub fn getFace(self: *Font) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("face")) orelse "";
|
||||
}
|
||||
|
||||
pub fn setFace(self: *Font, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("face"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub fn getSize(self: *Font) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("size")) orelse "";
|
||||
}
|
||||
|
||||
pub fn setSize(self: *Font, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("size"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Font);
|
||||
|
||||
@@ -22,4 +50,13 @@ pub const JsApi = struct {
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const color = bridge.accessor(Font.getColor, Font.setColor, .{ .ce_reactions = true });
|
||||
pub const face = bridge.accessor(Font.getFace, Font.setFace, .{ .ce_reactions = true });
|
||||
pub const size = bridge.accessor(Font.getSize, Font.setSize, .{ .ce_reactions = true });
|
||||
};
|
||||
|
||||
const testing = @import("../../../../testing.zig");
|
||||
test "WebApi: HTML.Font" {
|
||||
try testing.htmlRunner("element/html/font.html", .{});
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
@@ -14,6 +15,22 @@ pub fn asNode(self: *FrameSet) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub fn getCols(self: *FrameSet) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("cols")) orelse "";
|
||||
}
|
||||
|
||||
pub fn setCols(self: *FrameSet, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("cols"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub fn getRows(self: *FrameSet) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("rows")) orelse "";
|
||||
}
|
||||
|
||||
pub fn setRows(self: *FrameSet, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("rows"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(FrameSet);
|
||||
|
||||
@@ -22,6 +39,9 @@ pub const JsApi = struct {
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const cols = bridge.accessor(FrameSet.getCols, FrameSet.setCols, .{ .ce_reactions = true });
|
||||
pub const rows = bridge.accessor(FrameSet.getRows, FrameSet.setRows, .{ .ce_reactions = true });
|
||||
};
|
||||
|
||||
const testing = @import("../../../../testing.zig");
|
||||
|
||||
156
src/browser/webapi/element/html/Marquee.zig
Normal file
156
src/browser/webapi/element/html/Marquee.zig
Normal file
@@ -0,0 +1,156 @@
|
||||
const std = @import("std");
|
||||
|
||||
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");
|
||||
|
||||
const Marquee = @This();
|
||||
|
||||
_proto: *HtmlElement,
|
||||
|
||||
pub fn asElement(self: *Marquee) *Element {
|
||||
return self._proto._proto;
|
||||
}
|
||||
pub fn asNode(self: *Marquee) *Node {
|
||||
return self.asElement().asNode();
|
||||
}
|
||||
|
||||
pub fn getBehavior(self: *Marquee) []const u8 {
|
||||
return getEnumerated(self, "behavior", &.{ "scroll", "slide", "alternate" }, "scroll");
|
||||
}
|
||||
|
||||
pub fn setBehavior(self: *Marquee, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("behavior"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub fn getDirection(self: *Marquee) []const u8 {
|
||||
return getEnumerated(self, "direction", &.{ "up", "right", "down", "left" }, "left");
|
||||
}
|
||||
|
||||
pub fn setDirection(self: *Marquee, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("direction"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub fn getBgColor(self: *Marquee) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("bgcolor")) orelse "";
|
||||
}
|
||||
|
||||
pub fn setBgColor(self: *Marquee, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("bgcolor"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub fn getHeight(self: *Marquee) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("height")) orelse "";
|
||||
}
|
||||
|
||||
pub fn setHeight(self: *Marquee, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("height"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub fn getWidth(self: *Marquee) []const u8 {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("width")) orelse "";
|
||||
}
|
||||
|
||||
pub fn setWidth(self: *Marquee, value: []const u8, frame: *Frame) !void {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("width"), .wrap(value), frame);
|
||||
}
|
||||
|
||||
pub fn getHspace(self: *Marquee) u32 {
|
||||
return getU32(self, "hspace", 0);
|
||||
}
|
||||
|
||||
pub fn setHspace(self: *Marquee, value: u32, frame: *Frame) !void {
|
||||
try setU32(self, "hspace", value, 0, frame);
|
||||
}
|
||||
|
||||
pub fn getVspace(self: *Marquee) u32 {
|
||||
return getU32(self, "vspace", 0);
|
||||
}
|
||||
|
||||
pub fn setVspace(self: *Marquee, value: u32, frame: *Frame) !void {
|
||||
try setU32(self, "vspace", value, 0, frame);
|
||||
}
|
||||
|
||||
pub fn getScrollAmount(self: *Marquee) u32 {
|
||||
return getU32(self, "scrollamount", 6);
|
||||
}
|
||||
|
||||
pub fn setScrollAmount(self: *Marquee, value: u32, frame: *Frame) !void {
|
||||
try setU32(self, "scrollamount", value, 6, frame);
|
||||
}
|
||||
|
||||
pub fn getScrollDelay(self: *Marquee) u32 {
|
||||
return getU32(self, "scrolldelay", 85);
|
||||
}
|
||||
|
||||
pub fn setScrollDelay(self: *Marquee, value: u32, frame: *Frame) !void {
|
||||
try setU32(self, "scrolldelay", value, 85, frame);
|
||||
}
|
||||
|
||||
pub fn getTrueSpeed(self: *Marquee) bool {
|
||||
return self.asElement().getAttributeSafe(comptime .wrap("truespeed")) != null;
|
||||
}
|
||||
|
||||
pub fn setTrueSpeed(self: *Marquee, truespeed: bool, frame: *Frame) !void {
|
||||
if (truespeed) {
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("truespeed"), .wrap(""), frame);
|
||||
} else {
|
||||
try self.asElement().removeAttribute(comptime .wrap("truespeed"), frame);
|
||||
}
|
||||
}
|
||||
|
||||
fn getEnumerated(self: *Marquee, comptime attr: []const u8, keywords: []const []const u8, default: []const u8) []const u8 {
|
||||
const value = self.asElement().getAttributeSafe(comptime .wrap(attr)) orelse return default;
|
||||
for (keywords) |keyword| {
|
||||
if (std.ascii.eqlIgnoreCase(value, keyword)) return keyword;
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
// Reflects an `unsigned long` content attribute: parses with the "rules for
|
||||
// parsing non-negative integers" (the lax integer parser, then rejecting a
|
||||
// negative result), so a valid value lands in [0, 2147483647].
|
||||
fn getU32(self: *Marquee, comptime attr: []const u8, default: u32) u32 {
|
||||
const value = self.asElement().getAttributeSafe(comptime .wrap(attr)) orelse return default;
|
||||
const parsed = HtmlElement.parseInteger(value) orelse return default;
|
||||
|
||||
if (parsed < 0) {
|
||||
return default;
|
||||
}
|
||||
return @intCast(parsed);
|
||||
}
|
||||
|
||||
fn setU32(self: *Marquee, comptime attr: []const u8, value: u32, default: u32, frame: *Frame) !void {
|
||||
const written = if (value > 2147483647) default else value;
|
||||
var buf: [10]u8 = undefined;
|
||||
const str = std.fmt.bufPrint(&buf, "{d}", .{written}) catch unreachable;
|
||||
try self.asElement().setAttributeSafe(comptime .wrap(attr), .wrap(str), frame);
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Marquee);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "HTMLMarqueeElement";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const behavior = bridge.accessor(Marquee.getBehavior, Marquee.setBehavior, .{ .ce_reactions = true });
|
||||
pub const bgColor = bridge.accessor(Marquee.getBgColor, Marquee.setBgColor, .{ .ce_reactions = true });
|
||||
pub const direction = bridge.accessor(Marquee.getDirection, Marquee.setDirection, .{ .ce_reactions = true });
|
||||
pub const height = bridge.accessor(Marquee.getHeight, Marquee.setHeight, .{ .ce_reactions = true });
|
||||
pub const hspace = bridge.accessor(Marquee.getHspace, Marquee.setHspace, .{ .ce_reactions = true });
|
||||
pub const scrollAmount = bridge.accessor(Marquee.getScrollAmount, Marquee.setScrollAmount, .{ .ce_reactions = true });
|
||||
pub const scrollDelay = bridge.accessor(Marquee.getScrollDelay, Marquee.setScrollDelay, .{ .ce_reactions = true });
|
||||
pub const trueSpeed = bridge.accessor(Marquee.getTrueSpeed, Marquee.setTrueSpeed, .{ .ce_reactions = true });
|
||||
pub const vspace = bridge.accessor(Marquee.getVspace, Marquee.setVspace, .{ .ce_reactions = true });
|
||||
pub const width = bridge.accessor(Marquee.getWidth, Marquee.setWidth, .{ .ce_reactions = true });
|
||||
};
|
||||
|
||||
const testing = @import("../../../../testing.zig");
|
||||
test "WebApi: HTML.Marquee" {
|
||||
try testing.htmlRunner("element/html/marquee.html", .{});
|
||||
}
|
||||
Reference in New Issue
Block a user