@@ -21,6 +21,7 @@ const parser = @import("../netsurf.zig");
2121const generate = @import ("../../runtime/generate.zig" );
2222const Page = @import ("../page.zig" ).Page ;
2323
24+ const urlStitch = @import ("../../url.zig" ).URL .stitch ;
2425const URL = @import ("../url/url.zig" ).URL ;
2526const Node = @import ("../dom/node.zig" ).Node ;
2627const Element = @import ("../dom/element.zig" ).Element ;
@@ -630,7 +631,7 @@ pub const HTMLInputElement = struct {
630631 pub fn set_defaultChecked (self : * parser.Input , default_checked : bool ) ! void {
631632 try parser .inputSetDefaultChecked (self , default_checked );
632633 }
633- pub fn get_from (self : * parser.Input ) ! ? * parser.Form {
634+ pub fn get_form (self : * parser.Input ) ! ? * parser.Form {
634635 return try parser .inputGetForm (self );
635636 }
636637 pub fn get_accept (self : * parser.Input ) ! []const u8 {
@@ -660,7 +661,7 @@ pub const HTMLInputElement = struct {
660661 pub fn get_maxLength (self : * parser.Input ) ! i32 {
661662 return try parser .inputGetMaxLength (self );
662663 }
663- pub fn set_maxLength (self : * parser.Input , max_length : u32 ) ! void {
664+ pub fn set_maxLength (self : * parser.Input , max_length : i32 ) ! void {
664665 try parser .inputSetMaxLength (self , max_length );
665666 }
666667 pub fn get_name (self : * parser.Input ) ! []const u8 {
@@ -678,18 +679,22 @@ pub const HTMLInputElement = struct {
678679 pub fn get_size (self : * parser.Input ) ! u32 {
679680 return try parser .inputGetSize (self );
680681 }
681- pub fn set_size (self : * parser.Input , size : u32 ) ! void {
682+ pub fn set_size (self : * parser.Input , size : i32 ) ! void {
682683 try parser .inputSetSize (self , size );
683684 }
684685 pub fn get_src (self : * parser.Input ) ! []const u8 {
685686 return try parser .inputGetSrc (self );
686687 }
687- pub fn set_src (self : * parser.Input , src : []const u8 ) ! void {
688- try parser .inputSetSrc (self , src );
688+ pub fn set_src (self : * parser.Input , src : []const u8 , page : * Page ) ! void {
689+ const new_src = try urlStitch (page .call_arena , src , page .url .raw );
690+ try parser .inputSetSrc (self , new_src );
689691 }
690692 pub fn get_type (self : * parser.Input ) ! []const u8 {
691693 return try parser .inputGetType (self );
692694 }
695+ pub fn set_type (self : * parser.Input , type_ : []const u8 ) ! void {
696+ try parser .inputSetType (self , type_ );
697+ }
693698 pub fn get_value (self : * parser.Input ) ! []const u8 {
694699 return try parser .inputGetValue (self );
695700 }
@@ -1223,3 +1228,144 @@ test "Browser.HTML.Element" {
12231228 .{ "lyric.src" , "15" },
12241229 }, .{});
12251230}
1231+ test "Browser.HTML.Element.propeties" {
1232+ var runner = try testing .jsRunner (testing .tracking_allocator , .{ .url = "https://lightpanda.io/noslashattheend" });
1233+ defer runner .deinit ();
1234+ const bool_valids = [_ ]Valid {
1235+ .{ .input = "true" , .is_str = false },
1236+ .{ .input = "" , .is_str = true , .expected = "false" },
1237+ .{ .input = "13.5" , .is_str = true , .expected = "true" },
1238+ };
1239+ const str_valids = [_ ]Valid {
1240+ .{ .input = "foo" , .is_str = true },
1241+ .{ .input = "5" , .is_str = false , .expected = "5" },
1242+ .{ .input = "" , .is_str = true },
1243+ .{ .input = "document" , .is_str = false , .expected = "[object HTMLDocument]" },
1244+ };
1245+ // TODO these tests are mostly just data should we store them in Sqlite or so?
1246+ try testCreateElement (& runner , "input" );
1247+ // Valid input.form is tested separately :Browser.HTML.Element.propeties.input.form
1248+ try testProperty (& runner , "input" , "form" , "null" , "null" , &.{}, &.{.{ .input = "foo" , .is_str = true }});
1249+ try testProperty (& runner , "input" , "accept" , "" , "" , & str_valids , &.{});
1250+ try testProperty (& runner , "input" , "alt" , "" , "" , & str_valids , &.{});
1251+ try testProperty (& runner , "input" , "disabled" , "false" , "false" , & bool_valids , &.{});
1252+ try testProperty (& runner , "input" , "maxLength" , "-1" , "0" , &.{.{ .input = "5" , .is_str = false }}, &.{.{ .input = "banana" , .is_str = true }});
1253+ try testing .expectError (error .ExecutionError , runner .testCases (&.{.{ "elem_input.maxLength = -45" , null }}, .{}));
1254+ try testProperty (& runner , "input" , "name" , "" , "" , & str_valids , &.{});
1255+ try testProperty (& runner , "input" , "readOnly" , "false" , "false" , & bool_valids , &.{});
1256+ try testProperty (& runner , "input" , "size" , "20" , "20" , &.{.{ .input = "5" , .is_str = false }}, &.{.{ .input = "-26" , .is_str = false }});
1257+ try testing .expectError (error .ExecutionError , runner .testCases (&.{.{ "elem_input.size = 0" , null }}, .{}));
1258+ try testing .expectError (error .ExecutionError , runner .testCases (&.{.{ "elem_input.size = 'banana'" , null }}, .{}));
1259+ try testProperty (& runner , "input" , "src" , "" , "" , &.{
1260+ .{ .input = "foo" , .is_str = true , .expected = "https://lightpanda.io/foo" }, // TODO stitch should work with spaces -> %20
1261+ .{ .input = "-3" , .is_str = false , .expected = "https://lightpanda.io/-3" },
1262+ .{ .input = "" , .is_str = true , .expected = "https://lightpanda.io/noslashattheend" },
1263+ }, &.{});
1264+ try testProperty (& runner , "input" , "type" , "text" , "text" , &.{.{ .input = "checkbox" , .is_str = true }}, &.{.{ .input = "5" , .is_str = true }});
1265+
1266+ // Properties that are related
1267+ try runner .testCases (&.{
1268+ .{ "let input_checked = document.createElement('input')" , null },
1269+ .{ "input_checked.defaultChecked" , "false" },
1270+ .{ "input_checked.checked" , "false" },
1271+
1272+ .{ "input_checked.defaultChecked = true" , "true" },
1273+ .{ "input_checked.defaultChecked" , "true" },
1274+ .{ "input_checked.checked" , "true" }, // Also perceived as true
1275+
1276+ .{ "input_checked.checked = false" , "false" },
1277+ .{ "input_checked.defaultChecked" , "true" },
1278+ .{ "input_checked.checked" , "false" },
1279+
1280+ .{ "input_checked.defaultChecked = true" , "true" },
1281+ .{ "input_checked.checked" , "false" }, // Still false
1282+ }, .{});
1283+ try runner .testCases (&.{
1284+ .{ "let input_value = document.createElement('input')" , null },
1285+ .{ "input_value.defaultValue" , "" },
1286+ .{ "input_value.value" , "" },
1287+
1288+ .{ "input_value.defaultValue = 3.1" , "3.1" },
1289+ .{ "input_value.defaultValue" , "3.1" },
1290+ .{ "input_value.value" , "3.1" }, // Also perceived as 3.1
1291+
1292+ .{ "input_value.value = 'mango'" , "mango" },
1293+ .{ "input_value.defaultValue" , "3.1" },
1294+ .{ "input_value.value" , "mango" },
1295+
1296+ .{ "input_value.defaultValue = true" , "true" },
1297+ .{ "input_value.value" , "mango" }, // Still mango
1298+ }, .{});
1299+ }
1300+ test "Browser.HTML.Element.propeties.input.form" {
1301+ var runner = try testing .jsRunner (testing .tracking_allocator , .{ .html =
1302+ \\ <form action="test.php" target="_blank">
1303+ \\ <p>
1304+ \\ <label>First name: <input type="text" name="first-name" /></label>
1305+ \\ </p>
1306+ \\ </form>
1307+ });
1308+ defer runner .deinit ();
1309+
1310+ try runner .testCases (&.{
1311+ .{ "let elem_input = document.querySelector('input')" , null },
1312+ }, .{});
1313+ try testProperty (& runner , "input" , "form" , "[object HTMLFormElement]" , "[object HTMLFormElement]" , &.{}, &.{.{ .input = "5" , .is_str = false }});
1314+ }
1315+
1316+ const Valid = struct {
1317+ input : []const u8 ,
1318+ is_str : bool ,
1319+ expected : ? []const u8 = null , // Needed when input != expected
1320+ };
1321+ const Invalid = struct {
1322+ input : []const u8 ,
1323+ is_str : bool ,
1324+ };
1325+
1326+ fn testCreateElement (runner : * testing.JsRunner , comptime name : []const u8 ) ! void {
1327+ try runner .testCases (&.{
1328+ .{ "let elem_" ++ name ++ " = document.createElement('" ++ name ++ "')" , null },
1329+ }, .{});
1330+ }
1331+ // TODO reduce comptime
1332+ // Default is the expected value after creation and after setting an invalid value
1333+ // Valid input is expected to return itself or the expected value
1334+ // Invalid input is expected to return the default value
1335+ // .{ "elem.type", "text" }, // default
1336+ // .{ "elem.type = 'checkbox'", "checkbox" }, // valid
1337+ // .{ "elem.type", "checkbox" },
1338+ // .{ "elem.type = '5'", "5" }, // invalid
1339+ // .{ "elem.type", "text" },
1340+ fn testProperty (
1341+ runner : * testing.JsRunner ,
1342+ comptime name : []const u8 ,
1343+ comptime property : []const u8 ,
1344+ comptime initial : []const u8 ,
1345+ comptime default : []const u8 ,
1346+ comptime valids : []const Valid ,
1347+ comptime invalids : []const Invalid ,
1348+ ) ! void {
1349+ const elem_dot_prop = "elem_" ++ name ++ "." ++ property ;
1350+
1351+ try runner .testCases (&.{
1352+ .{ elem_dot_prop , initial },
1353+ }, .{});
1354+
1355+ inline for (valids ) | valid | {
1356+ const set_input = if (valid .is_str ) "'" ++ valid .input ++ "'" else valid .input ;
1357+ const expected = valid .expected orelse valid .input ;
1358+ try runner .testCases (&.{
1359+ .{ elem_dot_prop ++ " = " ++ set_input , null },
1360+ .{ elem_dot_prop , expected },
1361+ }, .{});
1362+ }
1363+
1364+ inline for (invalids ) | invalid | {
1365+ const set_input = if (invalid .is_str ) "'" ++ invalid .input ++ "'" else invalid .input ;
1366+ try runner .testCases (&.{
1367+ .{ elem_dot_prop ++ " = " ++ set_input , null },
1368+ .{ elem_dot_prop , default },
1369+ }, .{});
1370+ }
1371+ }
0 commit comments