diff --git a/src/browser/tests/element/html/htmlelement-props.html b/src/browser/tests/element/html/htmlelement-props.html
new file mode 100644
index 00000000..a008adc5
--- /dev/null
+++ b/src/browser/tests/element/html/htmlelement-props.html
@@ -0,0 +1,56 @@
+
+
+
+
Hidden div
+Visible div
+
+No tabindex
+
+
+
+
diff --git a/src/browser/tests/element/html/input-attrs.html b/src/browser/tests/element/html/input-attrs.html
new file mode 100644
index 00000000..3e3bf606
--- /dev/null
+++ b/src/browser/tests/element/html/input-attrs.html
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/tests/window/stubs.html b/src/browser/tests/window/stubs.html
new file mode 100644
index 00000000..74c8a944
--- /dev/null
+++ b/src/browser/tests/window/stubs.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig
index 6e184b21..e90bf4b0 100644
--- a/src/browser/webapi/Window.zig
+++ b/src/browser/webapi/Window.zig
@@ -704,6 +704,7 @@ fn getFunctionFromSetter(setter_: ?FunctionSetter) ?js.Function.Global {
};
}
+
pub const JsApi = struct {
pub const bridge = js.Bridge(Window);
@@ -775,12 +776,29 @@ pub const JsApi = struct {
pub const innerWidth = bridge.property(1920, .{ .template = false });
pub const innerHeight = bridge.property(1080, .{ .template = false });
+ pub const devicePixelRatio = bridge.property(1, .{ .template = false });
+
// This should return a window-like object in specific conditions. Would be
// pretty complicated to properly support I think.
pub const opener = bridge.property(null, .{ .template = false });
+
+ pub const alert = bridge.function(struct {
+ fn alert(_: *const Window, _: ?[]const u8) void {}
+ }.alert, .{});
+ pub const confirm = bridge.function(struct {
+ fn confirm(_: *const Window, _: ?[]const u8) bool {
+ return false;
+ }
+ }.confirm, .{});
+ pub const prompt = bridge.function(struct {
+ fn prompt(_: *const Window, _: ?[]const u8, _: ?[]const u8) ?[]const u8 {
+ return null;
+ }
+ }.prompt, .{});
};
const testing = @import("../../testing.zig");
test "WebApi: Window" {
try testing.htmlRunner("window", .{});
+ try testing.htmlRunner("window/stubs.html", .{});
}
diff --git a/src/browser/webapi/element/Html.zig b/src/browser/webapi/element/Html.zig
index 43417bdb..5f2cb867 100644
--- a/src/browser/webapi/element/Html.zig
+++ b/src/browser/webapi/element/Html.zig
@@ -342,6 +342,37 @@ pub fn click(self: *HtmlElement, page: *Page) !void {
try page._event_manager.dispatch(self.asEventTarget(), event);
}
+// TODO: Per spec, hidden is a tristate: true | false | "until-found".
+// We only support boolean for now; "until-found" would need bridge union support.
+pub fn getHidden(self: *HtmlElement) bool {
+ return self.asElement().getAttributeSafe(comptime .wrap("hidden")) != null;
+}
+
+pub fn setHidden(self: *HtmlElement, hidden: bool, page: *Page) !void {
+ if (hidden) {
+ try self.asElement().setAttributeSafe(comptime .wrap("hidden"), .wrap(""), page);
+ } else {
+ try self.asElement().removeAttribute(comptime .wrap("hidden"), page);
+ }
+}
+
+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,
+ };
+ };
+ return std.fmt.parseInt(i32, attr, 10) catch -1;
+}
+
+pub fn setTabIndex(self: *HtmlElement, value: i32, page: *Page) !void {
+ var buf: [12]u8 = undefined;
+ const str = std.fmt.bufPrint(&buf, "{d}", .{value}) catch unreachable;
+ try self.asElement().setAttributeSafe(comptime .wrap("tabindex"), .wrap(str), page);
+}
+
fn getAttributeFunction(
self: *HtmlElement,
listener_type: GlobalEventHandler,
@@ -1151,6 +1182,9 @@ pub const JsApi = struct {
pub const insertAdjacentHTML = bridge.function(HtmlElement.insertAdjacentHTML, .{ .dom_exception = true });
pub const click = bridge.function(HtmlElement.click, .{});
+ pub const hidden = bridge.accessor(HtmlElement.getHidden, HtmlElement.setHidden, .{});
+ pub const tabIndex = bridge.accessor(HtmlElement.getTabIndex, HtmlElement.setTabIndex, .{});
+
pub const onabort = bridge.accessor(HtmlElement.getOnAbort, HtmlElement.setOnAbort, .{});
pub const onanimationcancel = bridge.accessor(HtmlElement.getOnAnimationCancel, HtmlElement.setOnAnimationCancel, .{});
pub const onanimationend = bridge.accessor(HtmlElement.getOnAnimationEnd, HtmlElement.setOnAnimationEnd, .{});
@@ -1281,3 +1315,6 @@ const testing = @import("../../../testing.zig");
test "WebApi: HTML.event_listeners" {
try testing.htmlRunner("element/html/event_listeners.html", .{});
}
+test "WebApi: HTMLElement.props" {
+ try testing.htmlRunner("element/html/htmlelement-props.html", .{});
+}
diff --git a/src/browser/webapi/element/html/Input.zig b/src/browser/webapi/element/html/Input.zig
index 7ab4db6b..fcbb05bb 100644
--- a/src/browser/webapi/element/html/Input.zig
+++ b/src/browser/webapi/element/html/Input.zig
@@ -287,6 +287,58 @@ pub fn setRequired(self: *Input, required: bool, page: *Page) !void {
}
}
+pub fn getPlaceholder(self: *const Input) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("placeholder")) orelse "";
+}
+
+pub fn setPlaceholder(self: *Input, placeholder: []const u8, page: *Page) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("placeholder"), .wrap(placeholder), page);
+}
+
+pub fn getMin(self: *const Input) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("min")) orelse "";
+}
+
+pub fn setMin(self: *Input, min: []const u8, page: *Page) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("min"), .wrap(min), page);
+}
+
+pub fn getMax(self: *const Input) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("max")) orelse "";
+}
+
+pub fn setMax(self: *Input, max: []const u8, page: *Page) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("max"), .wrap(max), page);
+}
+
+pub fn getStep(self: *const Input) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("step")) orelse "";
+}
+
+pub fn setStep(self: *Input, step: []const u8, page: *Page) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("step"), .wrap(step), page);
+}
+
+pub fn getMultiple(self: *const Input) bool {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("multiple")) != null;
+}
+
+pub fn setMultiple(self: *Input, multiple: bool, page: *Page) !void {
+ if (multiple) {
+ try self.asElement().setAttributeSafe(comptime .wrap("multiple"), .wrap(""), page);
+ } else {
+ try self.asElement().removeAttribute(comptime .wrap("multiple"), page);
+ }
+}
+
+pub fn getAutocomplete(self: *const Input) []const u8 {
+ return self.asConstElement().getAttributeSafe(comptime .wrap("autocomplete")) orelse "";
+}
+
+pub fn setAutocomplete(self: *Input, autocomplete: []const u8, page: *Page) !void {
+ try self.asElement().setAttributeSafe(comptime .wrap("autocomplete"), .wrap(autocomplete), page);
+}
+
pub fn select(self: *Input, page: *Page) !void {
const len = if (self._value) |v| @as(u32, @intCast(v.len)) else 0;
try self.setSelectionRange(0, len, null, page);
@@ -564,6 +616,12 @@ pub const JsApi = struct {
pub const src = bridge.accessor(Input.getSrc, Input.setSrc, .{});
pub const form = bridge.accessor(Input.getForm, null, .{});
pub const indeterminate = bridge.accessor(Input.getIndeterminate, Input.setIndeterminate, .{});
+ pub const placeholder = bridge.accessor(Input.getPlaceholder, Input.setPlaceholder, .{});
+ pub const min = bridge.accessor(Input.getMin, Input.setMin, .{});
+ pub const max = bridge.accessor(Input.getMax, Input.setMax, .{});
+ pub const step = bridge.accessor(Input.getStep, Input.setStep, .{});
+ pub const multiple = bridge.accessor(Input.getMultiple, Input.setMultiple, .{});
+ pub const autocomplete = bridge.accessor(Input.getAutocomplete, Input.setAutocomplete, .{});
pub const select = bridge.function(Input.select, .{});
pub const selectionStart = bridge.accessor(Input.getSelectionStart, Input.setSelectionStart, .{});
@@ -662,4 +720,5 @@ test "WebApi: HTML.Input" {
try testing.htmlRunner("element/html/input.html", .{});
try testing.htmlRunner("element/html/input_click.html", .{});
try testing.htmlRunner("element/html/input_radio.html", .{});
+ try testing.htmlRunner("element/html/input-attrs.html", .{});
}