mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
add window stubs, HTMLElement hidden/tabIndex, input attribute reflections
- Window: alert/confirm/prompt (no-op stubs), devicePixelRatio - HTMLElement: hidden (boolean), tabIndex (integer) - Input: placeholder, min, max, step, multiple, autocomplete
This commit is contained in:
39
src/browser/tests/element/html/htmlelement-props.html
Normal file
39
src/browser/tests/element/html/htmlelement-props.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../../testing.js"></script>
|
||||
|
||||
<div id="d1" hidden>Hidden div</div>
|
||||
<div id="d2">Visible div</div>
|
||||
<input id="i1" tabindex="5">
|
||||
<div id="d3">No tabindex</div>
|
||||
|
||||
<script id="hidden">
|
||||
{
|
||||
const d1 = document.getElementById('d1');
|
||||
testing.expectEqual(true, d1.hidden);
|
||||
|
||||
d1.hidden = false;
|
||||
testing.expectEqual(false, d1.hidden);
|
||||
|
||||
const d2 = document.getElementById('d2');
|
||||
testing.expectEqual(false, d2.hidden);
|
||||
|
||||
d2.hidden = true;
|
||||
testing.expectEqual(true, d2.hidden);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="tabIndex">
|
||||
{
|
||||
const i1 = document.getElementById('i1');
|
||||
testing.expectEqual(5, i1.tabIndex);
|
||||
|
||||
i1.tabIndex = 10;
|
||||
testing.expectEqual(10, i1.tabIndex);
|
||||
|
||||
const d3 = document.getElementById('d3');
|
||||
testing.expectEqual(-1, d3.tabIndex);
|
||||
|
||||
d3.tabIndex = 0;
|
||||
testing.expectEqual(0, d3.tabIndex);
|
||||
}
|
||||
</script>
|
||||
87
src/browser/tests/element/html/input-attrs.html
Normal file
87
src/browser/tests/element/html/input-attrs.html
Normal file
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../../testing.js"></script>
|
||||
|
||||
<input id="i1" placeholder="Enter name" min="0" max="100" step="5" autocomplete="email">
|
||||
<input id="i2" type="file" multiple>
|
||||
<input id="i3">
|
||||
|
||||
<script id="placeholder">
|
||||
{
|
||||
const i1 = document.getElementById('i1');
|
||||
testing.expectEqual('Enter name', i1.placeholder);
|
||||
|
||||
i1.placeholder = 'Updated';
|
||||
testing.expectEqual('Updated', i1.placeholder);
|
||||
|
||||
const i3 = document.getElementById('i3');
|
||||
testing.expectEqual('', i3.placeholder);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="min">
|
||||
{
|
||||
const i1 = document.getElementById('i1');
|
||||
testing.expectEqual('0', i1.min);
|
||||
|
||||
i1.min = '10';
|
||||
testing.expectEqual('10', i1.min);
|
||||
|
||||
const i3 = document.getElementById('i3');
|
||||
testing.expectEqual('', i3.min);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="max">
|
||||
{
|
||||
const i1 = document.getElementById('i1');
|
||||
testing.expectEqual('100', i1.max);
|
||||
|
||||
i1.max = '200';
|
||||
testing.expectEqual('200', i1.max);
|
||||
|
||||
const i3 = document.getElementById('i3');
|
||||
testing.expectEqual('', i3.max);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="step">
|
||||
{
|
||||
const i1 = document.getElementById('i1');
|
||||
testing.expectEqual('5', i1.step);
|
||||
|
||||
i1.step = '0.5';
|
||||
testing.expectEqual('0.5', i1.step);
|
||||
|
||||
const i3 = document.getElementById('i3');
|
||||
testing.expectEqual('', i3.step);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="multiple">
|
||||
{
|
||||
const i2 = document.getElementById('i2');
|
||||
testing.expectEqual(true, i2.multiple);
|
||||
|
||||
i2.multiple = false;
|
||||
testing.expectEqual(false, i2.multiple);
|
||||
|
||||
const i3 = document.getElementById('i3');
|
||||
testing.expectEqual(false, i3.multiple);
|
||||
|
||||
i3.multiple = true;
|
||||
testing.expectEqual(true, i3.multiple);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="autocomplete">
|
||||
{
|
||||
const i1 = document.getElementById('i1');
|
||||
testing.expectEqual('email', i1.autocomplete);
|
||||
|
||||
i1.autocomplete = 'off';
|
||||
testing.expectEqual('off', i1.autocomplete);
|
||||
|
||||
const i3 = document.getElementById('i3');
|
||||
testing.expectEqual('', i3.autocomplete);
|
||||
}
|
||||
</script>
|
||||
34
src/browser/tests/window-stubs.html
Normal file
34
src/browser/tests/window-stubs.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="testing.js"></script>
|
||||
|
||||
<script id="alert">
|
||||
{
|
||||
// alert should be callable without error
|
||||
window.alert('hello');
|
||||
window.alert();
|
||||
testing.expectEqual(undefined, window.alert('test'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="confirm">
|
||||
{
|
||||
// confirm returns false in headless mode
|
||||
testing.expectEqual(false, window.confirm('proceed?'));
|
||||
testing.expectEqual(false, window.confirm());
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="prompt">
|
||||
{
|
||||
// prompt returns null in headless mode
|
||||
testing.expectEqual(null, window.prompt('enter value'));
|
||||
testing.expectEqual(null, window.prompt('enter value', 'default'));
|
||||
testing.expectEqual(null, window.prompt());
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="devicePixelRatio">
|
||||
{
|
||||
testing.expectEqual(1, window.devicePixelRatio);
|
||||
}
|
||||
</script>
|
||||
@@ -704,6 +704,15 @@ fn getFunctionFromSetter(setter_: ?FunctionSetter) ?js.Function.Global {
|
||||
};
|
||||
}
|
||||
|
||||
// Headless browser stubs: alert/confirm/prompt are no-ops
|
||||
fn jsAlert(_: *Window, _: ?[]const u8) void {}
|
||||
fn jsConfirm(_: *Window, _: ?[]const u8) bool {
|
||||
return false;
|
||||
}
|
||||
fn jsPrompt(_: *Window, _: ?[]const u8, _: ?[]const u8) ?[]const u8 {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(Window);
|
||||
|
||||
@@ -775,12 +784,19 @@ 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(Window.jsAlert, .{});
|
||||
pub const confirm = bridge.function(Window.jsConfirm, .{});
|
||||
pub const prompt = bridge.function(Window.jsPrompt, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "WebApi: Window" {
|
||||
try testing.htmlRunner("window", .{});
|
||||
try testing.htmlRunner("window-stubs.html", .{});
|
||||
}
|
||||
|
||||
@@ -342,6 +342,29 @@ pub fn click(self: *HtmlElement, page: *Page) !void {
|
||||
try page._event_manager.dispatch(self.asEventTarget(), event);
|
||||
}
|
||||
|
||||
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 return -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 return;
|
||||
try self.asElement().setAttributeSafe(comptime .wrap("tabindex"), .wrap(str), page);
|
||||
}
|
||||
|
||||
fn getAttributeFunction(
|
||||
self: *HtmlElement,
|
||||
listener_type: GlobalEventHandler,
|
||||
@@ -1151,6 +1174,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 +1307,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", .{});
|
||||
}
|
||||
|
||||
@@ -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", .{});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user