diff --git a/src/run_tests.zig b/src/run_tests.zig index ae5a247e..dea89d6f 100644 --- a/src/run_tests.zig +++ b/src/run_tests.zig @@ -23,6 +23,7 @@ const NodeListTestExecFn = @import("dom/nodelist.zig").testExecFn; const AttrTestExecFn = @import("dom/attribute.zig").testExecFn; const EventTargetTestExecFn = @import("dom/event_target.zig").testExecFn; const EventTestExecFn = @import("events/event.zig").testExecFn; +const xhr = @import("xhr/xhr.zig"); pub const Types = jsruntime.reflect(apiweb.Interfaces); @@ -146,3 +147,20 @@ test "Window is a libdom event target" { const et = @as(*parser.EventTarget, @ptrCast(&window)); _ = try parser.eventTargetDispatchEvent(et, event); } + +test "XMLHttpRequest.validMethod" { + // valid methods + for ([_][]const u8{ "get", "GET", "head", "HEAD" }) |tc| { + try xhr.XMLHttpRequest.validMethod(tc); + } + + // forbidden + for ([_][]const u8{ "connect", "CONNECT" }) |tc| { + try std.testing.expectError(parser.DOMError.Security, xhr.XMLHttpRequest.validMethod(tc)); + } + + // syntax + for ([_][]const u8{ "foo", "BAR" }) |tc| { + try std.testing.expectError(parser.DOMError.Syntax, xhr.XMLHttpRequest.validMethod(tc)); + } +} diff --git a/src/xhr/xhr.zig b/src/xhr/xhr.zig index e2cb0430..5abcec12 100644 --- a/src/xhr/xhr.zig +++ b/src/xhr/xhr.zig @@ -3,6 +3,8 @@ const std = @import("std"); const generate = @import("../generate.zig"); const EventTarget = @import("../dom/event_target.zig").EventTarget; +const DOMError = @import("../netsurf.zig").DOMError; + // XHR interfaces // https://xhr.spec.whatwg.org/#interface-xmlhttprequest pub const Interfaces = generate.Tuple(.{ @@ -49,10 +51,35 @@ pub const XMLHttpRequest = struct { password: ?[]const u8, ) !void { _ = self; - _ = method; _ = url; _ = asyn; _ = username; _ = password; + + // TODO If this’s relevant global object is a Window object and its + // associated Document is not fully active, then throw an + // "InvalidStateError" DOMException. + + try validMethod(method); + } + + const methods = [_][]const u8{ "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT" }; + const methods_forbidden = [_][]const u8{ "CONNECT", "TRACE", "TRACK" }; + + pub fn validMethod(m: []const u8) DOMError!void { + for (methods) |method| { + if (std.ascii.eqlIgnoreCase(method, m)) { + return; + } + } + // If method is a forbidden method, then throw a "SecurityError" DOMException. + for (methods_forbidden) |method| { + if (std.ascii.eqlIgnoreCase(method, m)) { + return DOMError.Security; + } + } + + // If method is not a method, then throw a "SyntaxError" DOMException. + return DOMError.Syntax; } };