diff --git a/src/TestHTTPServer.zig b/src/TestHTTPServer.zig new file mode 100644 index 000000000..11f44ee07 --- /dev/null +++ b/src/TestHTTPServer.zig @@ -0,0 +1,117 @@ +const std = @import("std"); + +const TestHTTPServer = @This(); + +shutdown: bool, +listener: ?std.net.Server, +handler: Handler, + +const Handler = *const fn (req: *std.http.Server.Request) anyerror!void; + +pub fn init(handler: Handler) TestHTTPServer { + return .{ + .shutdown = true, + .listener = null, + .handler = handler, + }; +} + +pub fn deinit(self: *TestHTTPServer) void { + self.shutdown = true; + if (self.listener) |*listener| { + listener.deinit(); + } +} + +pub fn run(self: *TestHTTPServer, wg: *std.Thread.WaitGroup) !void { + const address = try std.net.Address.parseIp("127.0.0.1", 9582); + + self.listener = try address.listen(.{ .reuse_address = true }); + var listener = &self.listener.?; + + wg.finish(); + + while (true) { + const conn = listener.accept() catch |err| { + if (self.shutdown) { + return; + } + return err; + }; + const thrd = try std.Thread.spawn(.{}, handleConnection, .{ self, conn }); + thrd.detach(); + } +} + +fn handleConnection(self: *TestHTTPServer, conn: std.net.Server.Connection) !void { + defer conn.stream.close(); + + var req_buf: [2048]u8 = undefined; + var conn_reader = conn.stream.reader(&req_buf); + var conn_writer = conn.stream.writer(&req_buf); + + var http_server = std.http.Server.init(conn_reader.interface(), &conn_writer.interface); + + while (true) { + var req = http_server.receiveHead() catch |err| switch (err) { + error.ReadFailed => continue, + error.HttpConnectionClosing => continue, + else => { + std.debug.print("Test HTTP Server error: {}\n", .{err}); + return err; + }, + }; + self.handler(&req) catch |err| { + std.debug.print("test http error '{s}': {}\n", .{ req.head.target, err }); + try req.respond("server error", .{ .status = .internal_server_error }); + return; + }; + } +} + +pub fn sendFile(req: *std.http.Server.Request, file_path: []const u8) !void { + var file = std.fs.cwd().openFile(file_path, .{}) catch |err| switch (err) { + error.FileNotFound => return req.respond("server error", .{ .status = .not_found }), + else => return err, + }; + + const stat = try file.stat(); + var send_buffer: [4096]u8 = undefined; + + var res = try req.respondStreaming(&send_buffer, .{ + .content_length = stat.size, + .respond_options = .{ + .extra_headers = &.{ + .{ .name = "content-type", .value = getContentType(file_path) }, + }, + }, + }); + + var read_buffer: [4096]u8 = undefined; + var reader = file.reader(&read_buffer); + _ = try res.writer.sendFileAll(&reader, .unlimited); + try res.writer.flush(); + try res.end(); +} + +fn getContentType(file_path: []const u8) []const u8 { + if (std.mem.endsWith(u8, file_path, ".js")) { + return "application/json"; + } + + if (std.mem.endsWith(u8, file_path, ".html")) { + return "text/html"; + } + + if (std.mem.endsWith(u8, file_path, ".htm")) { + return "text/html"; + } + + if (std.mem.endsWith(u8, file_path, ".xml")) { + // some wpt tests do this + return "text/xml"; + } + + std.debug.print("TestHTTPServer asked to serve an unknown file type: {s}\n", .{file_path}); + return "text/html"; +} diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index facd86a60..29e1e6a3f 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -403,14 +403,7 @@ fn startCallback(transfer: *Http.Transfer) !void { fn headerCallback(transfer: *Http.Transfer) !void { const script: *PendingScript = @ptrCast(@alignCast(transfer.ctx)); - script.headerCallback(transfer) catch |err| { - log.err(.http, "SM.headerCallback", .{ - .err = err, - .transfer = transfer, - .status = transfer.response_header.?.status, - }); - return err; - }; + script.headerCallback(transfer); } fn dataCallback(transfer: *Http.Transfer, data: []const u8) !void { @@ -463,18 +456,23 @@ const PendingScript = struct { log.debug(.http, "script fetch start", .{ .req = transfer }); } - fn headerCallback(self: *PendingScript, transfer: *Http.Transfer) !void { + fn headerCallback(self: *PendingScript, transfer: *Http.Transfer) void { const header = &transfer.response_header.?; + if (header.status != 200) { + log.info(.http, "script header", .{ + .req = transfer, + .status = header.status, + .content_type = header.contentType(), + }); + return; + } + log.debug(.http, "script header", .{ .req = transfer, .status = header.status, .content_type = header.contentType(), }); - if (header.status != 200) { - return error.InvalidStatusCode; - } - // If this isn't true, then we'll likely leak memory. If you don't // set `CURLOPT_SUPPRESS_CONNECT_HEADERS` and CONNECT to a proxy, this // will fail. This assertion exists to catch incorrect assumptions about diff --git a/src/browser/netsurf.zig b/src/browser/netsurf.zig index 6a9428af1..cba7b1ecc 100644 --- a/src/browser/netsurf.zig +++ b/src/browser/netsurf.zig @@ -107,7 +107,7 @@ inline fn strToData(s: *String) []const u8 { } inline fn strFromData(data: []const u8) !*String { - var s: ?*String = undefined; + var s: ?*String = null; const err = c.dom_string_create(data.ptr, data.len, &s); try DOMErr(err); return s.?; @@ -120,7 +120,7 @@ const LWCString = c.lwc_string; // } inline fn lwcStringFromData(data: []const u8) !*LWCString { - var s: ?*LWCString = undefined; + var s: ?*LWCString = null; const err = c.lwc_intern_string(data.ptr, data.len, &s); try DOMErr(err); return s.?; @@ -422,7 +422,7 @@ fn DOMErr(except: DOMException) DOMError!void { pub const Event = c.dom_event; pub fn eventCreate() !*Event { - var evt: ?*Event = undefined; + var evt: ?*Event = null; const err = c._dom_event_create(&evt); try DOMErr(err); return evt.?; @@ -445,7 +445,7 @@ pub fn eventInit(evt: *Event, typ: []const u8, opts: EventInit) !void { } pub fn eventType(evt: *Event) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = c._dom_event_get_type(evt, &s); try DOMErr(err); @@ -456,14 +456,14 @@ pub fn eventType(evt: *Event) ![]const u8 { } pub fn eventTarget(evt: *Event) !?*EventTarget { - var et: ?*EventTarget = undefined; + var et: ?*EventTarget = null; const err = c._dom_event_get_target(evt, &et); try DOMErr(err); return et; } pub fn eventCurrentTarget(evt: *Event) !?*EventTarget { - var et: ?*EventTarget = undefined; + var et: ?*EventTarget = null; const err = c._dom_event_get_current_target(evt, &et); try DOMErr(err); return et; @@ -556,14 +556,14 @@ pub fn eventToMutationEvent(evt: *Event) *MutationEvent { } pub fn mutationEventAttributeName(evt: *MutationEvent) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = c._dom_mutation_event_get_attr_name(evt, &s); try DOMErr(err); return strToData(s.?); } pub fn mutationEventPrevValue(evt: *MutationEvent) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = c._dom_mutation_event_get_prev_value(evt, &s); try DOMErr(err); if (s == null) return null; @@ -652,7 +652,7 @@ pub fn eventTargetAddEventListener( } }.handle; - var listener: ?*EventListener = undefined; + var listener: ?*EventListener = null; const errLst = c.dom_event_listener_create(event_handler, node, &listener); try DOMErr(errLst); defer c.dom_event_listener_unref(listener); @@ -673,8 +673,8 @@ pub fn eventTargetHasListener( const str = try strFromData(typ); var current: ?*EventListenerEntry = null; - var next: ?*EventListenerEntry = undefined; - var lst: ?*EventListener = undefined; + var next: ?*EventListenerEntry = null; + var lst: ?*EventListener = null; // iterate over the EventTarget's listeners while (true) { @@ -724,8 +724,8 @@ pub fn eventTargetRemoveEventListener( } pub fn eventTargetRemoveAllEventListeners(et: *EventTarget) !void { - var next: ?*EventListenerEntry = undefined; - var lst: ?*EventListener = undefined; + var next: ?*EventListenerEntry = null; + var lst: ?*EventListener = null; // iterate over the EventTarget's listeners while (true) { @@ -881,7 +881,7 @@ pub const EventTargetTBase = extern struct { pub const MouseEvent = c.dom_mouse_event; pub fn mouseEventCreate() !*MouseEvent { - var evt: ?*MouseEvent = undefined; + var evt: ?*MouseEvent = null; const err = c._dom_mouse_event_create(&evt); try DOMErr(err); return evt.?; @@ -936,7 +936,7 @@ pub fn mouseEventDefaultPrevented(evt: *MouseEvent) !bool { pub const KeyboardEvent = c.dom_keyboard_event; pub fn keyboardEventCreate() !*KeyboardEvent { - var evt: ?*KeyboardEvent = undefined; + var evt: ?*KeyboardEvent = null; const err = c._dom_keyboard_event_create(&evt); try DOMErr(err); return evt.?; @@ -979,7 +979,7 @@ pub fn keyboardEventInit(evt: *KeyboardEvent, typ: []const u8, opts: KeyboardEve } pub fn keyboardEventGetKey(evt: *KeyboardEvent) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; _ = c._dom_keyboard_event_get_key(evt, &s); return strToData(s.?); } @@ -1129,11 +1129,11 @@ fn nodeVtable(node: *Node) c.dom_node_vtable { } pub fn nodeLocalName(node: *Node) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = nodeVtable(node).dom_node_get_local_name.?(node, &s); try DOMErr(err); if (s == null) return ""; - var s_lower: ?*String = undefined; + var s_lower: ?*String = null; const errStr = c.dom_string_tolower(s, true, &s_lower); try DOMErr(errStr); return strToData(s_lower.?); @@ -1147,21 +1147,21 @@ pub fn nodeType(node: *Node) !NodeType { } pub fn nodeFirstChild(node: *Node) !?*Node { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = nodeVtable(node).dom_node_get_first_child.?(node, &res); try DOMErr(err); return res; } pub fn nodeLastChild(node: *Node) !?*Node { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = nodeVtable(node).dom_node_get_last_child.?(node, &res); try DOMErr(err); return res; } pub fn nodeNextSibling(node: *Node) !?*Node { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = nodeVtable(node).dom_node_get_next_sibling.?(node, &res); try DOMErr(err); return res; @@ -1182,7 +1182,7 @@ pub fn nodeNextElementSibling(node: *Node) !?*Element { } pub fn nodePreviousSibling(node: *Node) !?*Node { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = nodeVtable(node).dom_node_get_previous_sibling.?(node, &res); try DOMErr(err); return res; @@ -1203,7 +1203,7 @@ pub fn nodePreviousElementSibling(node: *Node) !?*Element { } pub fn nodeParentNode(node: *Node) !?*Node { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = nodeVtable(node).dom_node_get_parent_node.?(node, &res); try DOMErr(err); return res; @@ -1220,7 +1220,7 @@ pub fn nodeParentElement(node: *Node) !?*Element { } pub fn nodeName(node: *Node) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = nodeVtable(node).dom_node_get_node_name.?(node, &s); try DOMErr(err); if (s == null) return ""; @@ -1228,14 +1228,14 @@ pub fn nodeName(node: *Node) ![]const u8 { } pub fn nodeOwnerDocument(node: *Node) !?*Document { - var doc: ?*Document = undefined; + var doc: ?*Document = null; const err = nodeVtable(node).dom_node_get_owner_document.?(node, &doc); try DOMErr(err); return doc; } pub fn nodeValue(node: *Node) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = nodeVtable(node).dom_node_get_node_value.?(node, &s); try DOMErr(err); if (s == null) return null; @@ -1249,7 +1249,7 @@ pub fn nodeSetValue(node: *Node, value: []const u8) !void { } pub fn nodeTextContent(node: *Node) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = nodeVtable(node).dom_node_get_text_content.?(node, &s); try DOMErr(err); if (s == null) { @@ -1270,7 +1270,7 @@ pub fn nodeSetTextContent(node: *Node, value: []const u8) !void { } pub fn nodeGetChildNodes(node: *Node) !*NodeList { - var nlist: ?*NodeList = undefined; + var nlist: ?*NodeList = null; const err = nodeVtable(node).dom_node_get_child_nodes.?(node, &nlist); try DOMErr(err); return nlist.?; @@ -1288,14 +1288,14 @@ pub fn nodeGetRootNode(node: *Node) !*Node { } pub fn nodeAppendChild(node: *Node, child: *Node) !*Node { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = nodeVtable(node).dom_node_append_child.?(node, child, &res); try DOMErr(err); return res.?; } pub fn nodeCloneNode(node: *Node, is_deep: bool) !*Node { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = nodeVtable(node).dom_node_clone_node.?(node, is_deep, &res); try DOMErr(err); return res.?; @@ -1316,7 +1316,7 @@ pub fn nodeHasChildNodes(node: *Node) !bool { } pub fn nodeInsertBefore(node: *Node, new_node: *Node, ref_node: *Node) !*Node { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = nodeVtable(node).dom_node_insert_before.?(node, new_node, ref_node, &res); try DOMErr(err); return res.?; @@ -1345,7 +1345,7 @@ pub fn nodeIsSameNode(node: *Node, other: *Node) !bool { } pub fn nodeLookupPrefix(node: *Node, namespace: []const u8) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = nodeVtable(node).dom_node_lookup_prefix.?(node, try strFromData(namespace), &s); try DOMErr(err); if (s == null) return null; @@ -1353,7 +1353,7 @@ pub fn nodeLookupPrefix(node: *Node, namespace: []const u8) !?[]const u8 { } pub fn nodeLookupNamespaceURI(node: *Node, prefix_: ?[]const u8) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const prefix: ?*String = if (prefix_) |p| try strFromData(p) else null; const err = nodeVtable(node).dom_node_lookup_namespace.?(node, prefix, &s); try DOMErr(err); @@ -1367,14 +1367,14 @@ pub fn nodeNormalize(node: *Node) !void { } pub fn nodeRemoveChild(node: *Node, child: *Node) !*Node { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = nodeVtable(node).dom_node_remove_child.?(node, child, &res); try DOMErr(err); return res.?; } pub fn nodeReplaceChild(node: *Node, new_child: *Node, old_child: *Node) !*Node { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = nodeVtable(node).dom_node_replace_child.?(node, new_child, old_child, &res); try DOMErr(err); return res.?; @@ -1388,14 +1388,14 @@ pub fn nodeHasAttributes(node: *Node) !bool { } pub fn nodeGetAttributes(node: *Node) !?*NamedNodeMap { - var res: ?*NamedNodeMap = undefined; + var res: ?*NamedNodeMap = null; const err = nodeVtable(node).dom_node_get_attributes.?(node, &res); try DOMErr(err); return res; } pub fn nodeGetNamespace(node: *Node) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = nodeVtable(node).dom_node_get_namespace.?(node, &s); try DOMErr(err); if (s == null) return null; @@ -1403,7 +1403,7 @@ pub fn nodeGetNamespace(node: *Node) !?[]const u8 { } pub fn nodeGetPrefix(node: *Node) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = nodeVtable(node).dom_node_get_prefix.?(node, &s); try DOMErr(err); if (s == null) return null; @@ -1419,7 +1419,7 @@ pub fn nodeSetEmbedderData(node: *Node, data: *anyopaque) void { } pub fn nodeGetElementById(node: *Node, id: []const u8) !?*Element { - var el: ?*Element = undefined; + var el: ?*Element = null; const str_id = try strFromData(id); try DOMErr(c._dom_find_element_by_id(node, str_id, &el)); return el; @@ -1456,7 +1456,7 @@ pub inline fn characterDataToNode(cdata: *CharacterData) *Node { } pub fn characterDataData(cdata: *CharacterData) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = characterDataVtable(cdata).dom_characterdata_get_data.?(cdata, &s); try DOMErr(err); return strToData(s.?); @@ -1499,7 +1499,7 @@ pub fn characterDataReplaceData(cdata: *CharacterData, offset: u32, count: u32, } pub fn characterDataSubstringData(cdata: *CharacterData, offset: u32, count: u32) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = characterDataVtable(cdata).dom_characterdata_substring_data.?(cdata, offset, count, &s); try DOMErr(err); return strToData(s.?); @@ -1516,14 +1516,14 @@ fn textVtable(text: *Text) c.dom_text_vtable { } pub fn textWholdeText(text: *Text) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = textVtable(text).dom_text_get_whole_text.?(text, &s); try DOMErr(err); return strToData(s.?); } pub fn textSplitText(text: *Text, offset: u32) !*Text { - var res: ?*Text = undefined; + var res: ?*Text = null; const err = textVtable(text).dom_text_split_text.?(text, offset, &res); try DOMErr(err); return res.?; @@ -1541,7 +1541,7 @@ pub inline fn processingInstructionToNode(pi: *ProcessingInstruction) *Node { } pub fn processInstructionCopy(pi: *ProcessingInstruction) !*ProcessingInstruction { - var res: ?*Node = undefined; + var res: ?*Node = null; const err = c._dom_pi_copy(processingInstructionToNode(pi), &res); try DOMErr(err); return @as(*ProcessingInstruction, @ptrCast(res.?)); @@ -1555,7 +1555,7 @@ fn attributeVtable(a: *Attribute) c.dom_attr_vtable { } pub fn attributeGetName(a: *Attribute) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = attributeVtable(a).dom_attr_get_name.?(a, &s); try DOMErr(err); @@ -1563,7 +1563,7 @@ pub fn attributeGetName(a: *Attribute) ![]const u8 { } pub fn attributeGetValue(a: *Attribute) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = attributeVtable(a).dom_attr_get_value.?(a, &s); try DOMErr(err); if (s == null) return null; @@ -1582,7 +1582,7 @@ pub fn attributeSetValue(a: *Attribute, v: []const u8) !void { } pub fn attributeGetOwnerElement(a: *Attribute) !?*Element { - var elt: ?*Element = undefined; + var elt: ?*Element = null; const err = attributeVtable(a).dom_attr_get_owner_element.?(a, &elt); try DOMErr(err); if (elt == null) return null; @@ -1608,7 +1608,7 @@ pub fn elementTag(elem: *Element) !Tag { } pub fn elementGetTagName(elem: *Element) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = elementVtable(elem).dom_element_get_tag_name.?(elem, &s); try DOMErr(err); if (s == null) return null; @@ -1617,7 +1617,7 @@ pub fn elementGetTagName(elem: *Element) !?[]const u8 { } pub fn elementGetAttribute(elem: *Element, name: []const u8) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = elementVtable(elem).dom_element_get_attribute.?(elem, try strFromData(name), &s); try DOMErr(err); if (s == null) return null; @@ -1626,7 +1626,7 @@ pub fn elementGetAttribute(elem: *Element, name: []const u8) !?[]const u8 { } pub fn elementGetAttributeNS(elem: *Element, ns: []const u8, name: []const u8) !?[]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = elementVtable(elem).dom_element_get_attribute_ns.?( elem, try strFromData(ns), @@ -1702,14 +1702,14 @@ pub fn elementHasAttributeNS(elem: *Element, ns: []const u8, qname: []const u8) } pub fn elementGetAttributeNode(elem: *Element, name: []const u8) !?*Attribute { - var a: ?*Attribute = undefined; + var a: ?*Attribute = null; const err = elementVtable(elem).dom_element_get_attribute_node.?(elem, try strFromData(name), &a); try DOMErr(err); return a; } pub fn elementGetAttributeNodeNS(elem: *Element, ns: []const u8, name: []const u8) !?*Attribute { - var a: ?*Attribute = undefined; + var a: ?*Attribute = null; const err = elementVtable(elem).dom_element_get_attribute_node_ns.?( elem, if (ns.len == 0) null else try strFromData(ns), @@ -1721,21 +1721,21 @@ pub fn elementGetAttributeNodeNS(elem: *Element, ns: []const u8, name: []const u } pub fn elementSetAttributeNode(elem: *Element, attr: *Attribute) !?*Attribute { - var a: ?*Attribute = undefined; + var a: ?*Attribute = null; const err = elementVtable(elem).dom_element_set_attribute_node.?(elem, attr, &a); try DOMErr(err); return a; } pub fn elementSetAttributeNodeNS(elem: *Element, attr: *Attribute) !?*Attribute { - var a: ?*Attribute = undefined; + var a: ?*Attribute = null; const err = elementVtable(elem).dom_element_set_attribute_node_ns.?(elem, attr, &a); try DOMErr(err); return a; } pub fn elementRemoveAttributeNode(elem: *Element, attr: *Attribute) !*Attribute { - var a: ?*Attribute = undefined; + var a: ?*Attribute = null; const err = elementVtable(elem).dom_element_remove_attribute_node.?(elem, attr, &a); try DOMErr(err); return a.?; @@ -1761,7 +1761,7 @@ pub inline fn elementToNode(e: *Element) *Node { pub const TokenList = c.dom_tokenlist; pub fn tokenListCreate(elt: *Element, attr: []const u8) !*TokenList { - var list: ?*TokenList = undefined; + var list: ?*TokenList = null; const err = c.dom_tokenlist_create(elt, try strFromData(attr), &list); try DOMErr(err); return list.?; @@ -1775,7 +1775,7 @@ pub fn tokenListGetLength(l: *TokenList) !u32 { } pub fn tokenListItem(l: *TokenList, index: u32) !?[]const u8 { - var res: ?*String = undefined; + var res: ?*String = null; const err = c._dom_tokenlist_item(l, index, &res); try DOMErr(err); if (res == null) return null; @@ -1800,7 +1800,7 @@ pub fn tokenListRemove(l: *TokenList, token: []const u8) !void { } pub fn tokenListGetValue(l: *TokenList) !?[]const u8 { - var res: ?*String = undefined; + var res: ?*String = null; const err = c.dom_tokenlist_get_value(l, &res); try DOMErr(err); if (res == null) return null; @@ -1834,7 +1834,7 @@ pub inline fn anchorToNode(a: *Anchor) *Node { } pub fn anchorGetTarget(a: *Anchor) ![]const u8 { - var res: ?*String = undefined; + var res: ?*String = null; const err = c.dom_html_anchor_element_get_target(a, &res); try DOMErr(err); if (res == null) return ""; @@ -1847,7 +1847,7 @@ pub fn anchorSetTarget(a: *Anchor, target: []const u8) !void { } pub fn anchorGetHref(a: *Anchor) ![]const u8 { - var res: ?*String = undefined; + var res: ?*String = null; const err = c.dom_html_anchor_element_get_href(a, &res); try DOMErr(err); if (res == null) return ""; @@ -1860,7 +1860,7 @@ pub fn anchorSetHref(a: *Anchor, href: []const u8) !void { } pub fn anchorGetHrefLang(a: *Anchor) ![]const u8 { - var res: ?*String = undefined; + var res: ?*String = null; const err = c.dom_html_anchor_element_get_hreflang(a, &res); try DOMErr(err); if (res == null) return ""; @@ -1873,7 +1873,7 @@ pub fn anchorSetHrefLang(a: *Anchor, href: []const u8) !void { } pub fn anchorGetType(a: *Anchor) ![]const u8 { - var res: ?*String = undefined; + var res: ?*String = null; const err = c.dom_html_anchor_element_get_type(a, &res); try DOMErr(err); if (res == null) return ""; @@ -1886,7 +1886,7 @@ pub fn anchorSetType(a: *Anchor, t: []const u8) !void { } pub fn anchorGetRel(a: *Anchor) ![]const u8 { - var res: ?*String = undefined; + var res: ?*String = null; const err = c.dom_html_anchor_element_get_rel(a, &res); try DOMErr(err); if (res == null) return ""; @@ -1901,7 +1901,7 @@ pub fn anchorSetRel(a: *Anchor, rel: []const u8) !void { // HTMLLinkElement pub fn linkGetHref(link: *Link) ![]const u8 { - var res: ?*String = undefined; + var res: ?*String = null; const err = c.dom_html_link_element_get_href(link, &res); try DOMErr(err); if (res == null) return ""; @@ -1994,7 +1994,7 @@ pub inline fn documentFragmentToNode(doc: *DocumentFragment) *Node { } pub fn documentFragmentGetHost(frag: *DocumentFragment) ?*Node { - var node: ?*NodeExternal = undefined; + var node: ?*NodeExternal = null; c._dom_document_fragment_get_host(frag, &node); return if (node) |n| @ptrCast(n) else null; } @@ -2021,21 +2021,21 @@ fn documentTypeVtable(dt: *DocumentType) c.dom_document_type_vtable { } pub inline fn documentTypeGetName(dt: *DocumentType) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = documentTypeVtable(dt).dom_document_type_get_name.?(dt, &s); try DOMErr(err); return strToData(s.?); } pub inline fn documentTypeGetPublicId(dt: *DocumentType) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = documentTypeVtable(dt).dom_document_type_get_public_id.?(dt, &s); try DOMErr(err); return strToData(s.?); } pub inline fn documentTypeGetSystemId(dt: *DocumentType) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = documentTypeVtable(dt).dom_document_type_get_system_id.?(dt, &s); try DOMErr(err); return strToData(s.?); @@ -2047,7 +2047,7 @@ pub inline fn domImplementationCreateDocument( qname: ?[:0]const u8, doctype: ?*DocumentType, ) !*Document { - var doc: ?*Document = undefined; + var doc: ?*Document = null; var ptrnamespace: [*c]const u8 = null; if (namespace) |ns| { @@ -2077,7 +2077,7 @@ pub inline fn domImplementationCreateDocumentType( publicId: [:0]const u8, systemId: [:0]const u8, ) !*DocumentType { - var dt: ?*DocumentType = undefined; + var dt: ?*DocumentType = null; const err = c.dom_implementation_create_document_type(qname.ptr, publicId.ptr, systemId.ptr, &dt); try DOMErr(err); return dt.?; @@ -2119,14 +2119,14 @@ pub inline fn documentToNode(doc: *Document) *Node { } pub inline fn documentGetElementById(doc: *Document, id: []const u8) !?*Element { - var elem: ?*Element = undefined; + var elem: ?*Element = null; const err = documentVtable(doc).dom_document_get_element_by_id.?(doc, try strFromData(id), &elem); try DOMErr(err); return elem; } pub inline fn documentGetElementsByTagName(doc: *Document, tagname: []const u8) !*NodeList { - var nlist: ?*NodeList = undefined; + var nlist: ?*NodeList = null; const err = documentVtable(doc).dom_document_get_elements_by_tag_name.?(doc, try strFromData(tagname), &nlist); try DOMErr(err); return nlist.?; @@ -2134,7 +2134,7 @@ pub inline fn documentGetElementsByTagName(doc: *Document, tagname: []const u8) // documentGetDocumentElement returns the root document element. pub inline fn documentGetDocumentElement(doc: *Document) !?*Element { - var elem: ?*Element = undefined; + var elem: ?*Element = null; const err = documentVtable(doc).dom_document_get_document_element.?(doc, &elem); try DOMErr(err); if (elem == null) return null; @@ -2142,7 +2142,7 @@ pub inline fn documentGetDocumentElement(doc: *Document) !?*Element { } pub inline fn documentGetDocumentURI(doc: *Document) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = documentVtable(doc).dom_document_get_uri.?(doc, &s); try DOMErr(err); return strToData(s.?); @@ -2154,7 +2154,7 @@ pub fn documentSetDocumentURI(doc: *Document, uri: []const u8) !void { } pub inline fn documentGetInputEncoding(doc: *Document) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = documentVtable(doc).dom_document_get_input_encoding.?(doc, &s); try DOMErr(err); return strToData(s.?); @@ -2166,7 +2166,7 @@ pub inline fn documentSetInputEncoding(doc: *Document, enc: []const u8) !void { } pub inline fn documentCreateDocument(title: ?[]const u8) !*DocumentHTML { - var doc: ?*Document = undefined; + var doc: ?*Document = null; const err = c.dom_implementation_create_document( c.DOM_IMPLEMENTATION_HTML, null, @@ -2185,7 +2185,7 @@ pub inline fn documentCreateDocument(title: ?[]const u8) !*DocumentHTML { fn documentCreateHTMLElement(doc: *Document, tag_name: []const u8) !*Element { std.debug.assert(doc.is_html); - var elem: ?*Element = undefined; + var elem: ?*Element = null; const err = c._dom_html_document_create_element(doc, try strFromData(tag_name), &elem); try DOMErr(err); return elem.?; @@ -2196,7 +2196,7 @@ pub fn documentCreateElement(doc: *Document, tag_name: []const u8) !*Element { return documentCreateHTMLElement(doc, tag_name); } - var elem: ?*Element = undefined; + var elem: ?*Element = null; const err = documentVtable(doc).dom_document_create_element.?(doc, try strFromData(tag_name), &elem); try DOMErr(err); return elem.?; @@ -2205,7 +2205,7 @@ pub fn documentCreateElement(doc: *Document, tag_name: []const u8) !*Element { fn documentCreateHTMLElementNS(doc: *Document, ns: []const u8, tag_name: []const u8) !*Element { std.debug.assert(doc.is_html); - var elem: ?*Element = undefined; + var elem: ?*Element = null; const err = c._dom_html_document_create_element_ns( doc, try strFromData(ns), @@ -2221,7 +2221,7 @@ pub fn documentCreateElementNS(doc: *Document, ns: []const u8, tag_name: []const return documentCreateHTMLElementNS(doc, ns, tag_name); } - var elem: ?*Element = undefined; + var elem: ?*Element = null; const err = documentVtable(doc).dom_document_create_element_ns.?( doc, try strFromData(ns), @@ -2233,42 +2233,42 @@ pub fn documentCreateElementNS(doc: *Document, ns: []const u8, tag_name: []const } pub inline fn documentGetDoctype(doc: *Document) !?*DocumentType { - var dt: ?*DocumentType = undefined; + var dt: ?*DocumentType = null; const err = documentVtable(doc).dom_document_get_doctype.?(doc, &dt); try DOMErr(err); return dt; } pub inline fn documentCreateDocumentFragment(doc: *Document) !*DocumentFragment { - var df: ?*DocumentFragment = undefined; + var df: ?*DocumentFragment = null; const err = documentVtable(doc).dom_document_create_document_fragment.?(doc, &df); try DOMErr(err); return df.?; } pub inline fn documentCreateTextNode(doc: *Document, s: []const u8) !*Text { - var txt: ?*Text = undefined; + var txt: ?*Text = null; const err = documentVtable(doc).dom_document_create_text_node.?(doc, try strFromData(s), &txt); try DOMErr(err); return txt.?; } pub inline fn documentCreateCDATASection(doc: *Document, s: []const u8) !*CDATASection { - var cdata: ?*CDATASection = undefined; + var cdata: ?*CDATASection = null; const err = documentVtable(doc).dom_document_create_cdata_section.?(doc, try strFromData(s), &cdata); try DOMErr(err); return cdata.?; } pub inline fn documentCreateComment(doc: *Document, s: []const u8) !*Comment { - var com: ?*Comment = undefined; + var com: ?*Comment = null; const err = documentVtable(doc).dom_document_create_comment.?(doc, try strFromData(s), &com); try DOMErr(err); return com.?; } pub inline fn documentCreateProcessingInstruction(doc: *Document, target: []const u8, data: []const u8) !*ProcessingInstruction { - var pi: ?*ProcessingInstruction = undefined; + var pi: ?*ProcessingInstruction = null; const err = documentVtable(doc).dom_document_create_processing_instruction.?( doc, try strFromData(target), @@ -2296,14 +2296,14 @@ pub inline fn documentAdoptNode(doc: *Document, node: *Node) !*Node { } pub inline fn documentCreateAttribute(doc: *Document, name: []const u8) !*Attribute { - var attr: ?*Attribute = undefined; + var attr: ?*Attribute = null; const err = documentVtable(doc).dom_document_create_attribute.?(doc, try strFromData(name), &attr); try DOMErr(err); return attr.?; } pub inline fn documentCreateAttributeNS(doc: *Document, ns: []const u8, qname: []const u8) !*Attribute { - var attr: ?*Attribute = undefined; + var attr: ?*Attribute = null; const err = documentVtable(doc).dom_document_create_attribute_ns.?( doc, try strFromData(ns), @@ -2481,7 +2481,7 @@ pub inline fn documentHTMLToDocument(doc_html: *DocumentHTML) *Document { } pub inline fn documentHTMLBody(doc_html: *DocumentHTML) !?*Body { - var body: ?*ElementHTML = undefined; + var body: ?*ElementHTML = null; const err = documentHTMLVtable(doc_html).get_body.?(doc_html, &body); try DOMErr(err); if (body == null) return null; @@ -2498,7 +2498,7 @@ pub inline fn documentHTMLSetBody(doc_html: *DocumentHTML, elt: ?*ElementHTML) ! } pub inline fn documentHTMLGetReferrer(doc: *DocumentHTML) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = documentHTMLVtable(doc).get_referrer.?(doc, &s); try DOMErr(err); if (s == null) return ""; @@ -2506,7 +2506,7 @@ pub inline fn documentHTMLGetReferrer(doc: *DocumentHTML) ![]const u8 { } pub inline fn documentHTMLGetTitle(doc: *DocumentHTML) ![]const u8 { - var s: ?*String = undefined; + var s: ?*String = null; const err = documentHTMLVtable(doc).get_title.?(doc, &s); try DOMErr(err); if (s == null) return ""; @@ -2526,7 +2526,7 @@ pub fn documentHTMLSetCurrentScript(doc: *DocumentHTML, script: ?*Script) !void } pub fn documentHTMLGetCurrentScript(doc: *DocumentHTML) !?*Script { - var elem: ?*ElementHTML = undefined; + var elem: ?*ElementHTML = null; const err = documentHTMLVtable(doc).get_current_script.?(doc, &elem); try DOMErr(err); if (elem == null) return null; @@ -2540,7 +2540,7 @@ pub fn documentHTMLSetLocation(T: type, doc: *DocumentHTML, location: *T) !void } pub fn documentHTMLGetLocation(T: type, doc: *DocumentHTML) !?*T { - var l: ?*anyopaque = undefined; + var l: ?*anyopaque = null; const err = documentHTMLVtable(doc).get_location.?(doc, &l); try DOMErr(err); @@ -2666,7 +2666,7 @@ pub fn optionCollectionGetLength(collection: *OptionCollection) !u32 { } pub fn optionCollectionItem(collection: *OptionCollection, index: u32) !*Option { - var node: ?*NodeExternal = undefined; + var node: ?*NodeExternal = null; const err = c.dom_html_options_collection_item(collection, index, &node); try DOMErr(err); return @ptrCast(node.?); @@ -2747,7 +2747,7 @@ pub fn htmlCollectionGetLength(collection: *HTMLCollection) !u32 { } pub fn htmlCollectionItem(collection: *HTMLCollection, index: u32) !*Node { - var node: ?*NodeExternal = undefined; + var node: ?*NodeExternal = null; const err = c.dom_html_collection_item(collection, index, &node); try DOMErr(err); return @ptrCast(node.?); diff --git a/src/browser/xhr/xhr.zig b/src/browser/xhr/xhr.zig index 76ca98e96..229d6ff74 100644 --- a/src/browser/xhr/xhr.zig +++ b/src/browser/xhr/xhr.zig @@ -785,8 +785,7 @@ test "Browser.XHR.XMLHttpRequest" { .{ "req.statusText", "OK" }, .{ "req.getResponseHeader('Content-Type')", "text/html; charset=utf-8" }, .{ "req.getAllResponseHeaders()", "content-length: 100\r\n" ++ - "Content-Type: text/html; charset=utf-8\r\n" ++ - "Connection: Close\r\n" }, + "Content-Type: text/html; charset=utf-8\r\n" }, .{ "req.responseText.length", "100" }, .{ "req.response.length == req.responseText.length", "true" }, .{ "req.responseXML instanceof Document", "true" }, diff --git a/src/main.zig b/src/main.zig index fb02932f0..35bcf7df5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -719,23 +719,26 @@ test { std.testing.refAllDecls(@This()); } +const TestHTTPServer = @import("TestHTTPServer.zig"); + var test_cdp_server: ?Server = null; +var test_http_server: ?TestHTTPServer = null; + test "tests:beforeAll" { log.opts.level = .err; log.opts.format = .logfmt; - try testing.setup(); - var wg: std.Thread.WaitGroup = .{}; wg.startMany(2); { - const thread = try std.Thread.spawn(.{}, serveHTTP, .{&wg}); + const thread = try std.Thread.spawn(.{}, serveCDP, .{&wg}); thread.detach(); } + test_http_server = TestHTTPServer.init(testHTTPHandler); { - const thread = try std.Thread.spawn(.{}, serveCDP, .{&wg}); + const thread = try std.Thread.spawn(.{}, TestHTTPServer.run, .{ &test_http_server.?, &wg }); thread.detach(); } @@ -748,59 +751,10 @@ test "tests:afterAll" { if (test_cdp_server) |*server| { server.deinit(); } - testing.shutdown(); -} - -fn serveHTTP(wg: *std.Thread.WaitGroup) !void { - const address = try std.net.Address.parseIp("127.0.0.1", 9582); - - var listener = try address.listen(.{ .reuse_address = true }); - defer listener.deinit(); - - wg.finish(); - - var buf: [1024]u8 = undefined; - while (true) { - var conn = try listener.accept(); - defer conn.stream.close(); - var conn_reader = conn.stream.reader(&buf); - var conn_writer = conn.stream.writer(&buf); - - var http_server = std.http.Server.init(conn_reader.interface(), &conn_writer.interface); - - var request = http_server.receiveHead() catch |err| switch (err) { - error.HttpConnectionClosing => continue, - else => { - std.debug.print("Test HTTP Server error: {}\n", .{err}); - return err; - }, - }; - - const path = request.head.target; - - if (std.mem.eql(u8, path, "/loader")) { - try request.respond("Hello!", .{ - .extra_headers = &.{.{ .name = "Connection", .value = "close" }}, - }); - } else if (std.mem.eql(u8, path, "/xhr")) { - try request.respond("1234567890" ** 10, .{ - .extra_headers = &.{ - .{ .name = "Content-Type", .value = "text/html; charset=utf-8" }, - .{ .name = "Connection", .value = "Close" }, - }, - }); - } else if (std.mem.eql(u8, path, "/xhr/json")) { - try request.respond("{\"over\":\"9000!!!\"}", .{ - .extra_headers = &.{ - .{ .name = "Content-Type", .value = "application/json" }, - .{ .name = "Connection", .value = "Close" }, - }, - }); - } else { - // should not have an unknown path - unreachable; - } + if (test_http_server) |*server| { + server.deinit(); } + testing.shutdown(); } fn serveCDP(wg: *std.Thread.WaitGroup) !void { @@ -816,3 +770,31 @@ fn serveCDP(wg: *std.Thread.WaitGroup) !void { return err; }; } + +fn testHTTPHandler(req: *std.http.Server.Request) !void { + const path = req.head.target; + + if (std.mem.eql(u8, path, "/loader")) { + return req.respond("Hello!", .{ + .extra_headers = &.{.{ .name = "Connection", .value = "close" }}, + }); + } + + if (std.mem.eql(u8, path, "/xhr")) { + return req.respond("1234567890" ** 10, .{ + .extra_headers = &.{ + .{ .name = "Content-Type", .value = "text/html; charset=utf-8" }, + }, + }); + } + + if (std.mem.eql(u8, path, "/xhr/json")) { + return req.respond("{\"over\":\"9000!!!\"}", .{ + .extra_headers = &.{ + .{ .name = "Content-Type", .value = "application/json" }, + }, + }); + } + + unreachable; +} diff --git a/src/main_wpt.zig b/src/main_wpt.zig index f70aaed39..7a4f9d8ef 100644 --- a/src/main_wpt.zig +++ b/src/main_wpt.zig @@ -22,24 +22,34 @@ const log = @import("log.zig"); const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; +const App = @import("app.zig").App; const Env = @import("browser/env.zig").Env; -const Platform = @import("runtime/js.zig").Platform; +const Browser = @import("browser/browser.zig").Browser; +const Session = @import("browser/session.zig").Session; +const TestHTTPServer = @import("TestHTTPServer.zig"); const parser = @import("browser/netsurf.zig"); const polyfill = @import("browser/polyfill/polyfill.zig"); const WPT_DIR = "tests/wpt"; -// TODO For now the WPT tests run is specific to WPT. -// It manually load js framwork libs, and run the first script w/ js content in -// the HTML page. -// Once lightpanda will have the html loader, it would be useful to refactor -// this test to use it. pub fn main() !void { var gpa: std.heap.DebugAllocator(.{}) = .init; defer _ = gpa.deinit(); + const allocator = gpa.allocator(); - log.opts.level = .warn; + log.opts.level = .err; + + var http_server = TestHTTPServer.init(httpHandler); + defer http_server.deinit(); + + { + var wg: std.Thread.WaitGroup = .{}; + wg.startMany(1); + var thrd = try std.Thread.spawn(.{}, TestHTTPServer.run, .{ &http_server, &wg }); + thrd.detach(); + wg.wait(); + } // An arena for the runner itself, lives for the duration of the the process var ra = ArenaAllocator.init(allocator); @@ -48,29 +58,33 @@ pub fn main() !void { const cmd = try parseArgs(runner_arena); - try @import("testing.zig").setup(); - defer @import("testing.zig").shutdown(); - - // prepare libraries to load on each test case. - var loader = FileLoader.init(runner_arena, WPT_DIR); - - var it = try TestIterator.init(runner_arena, WPT_DIR, cmd.filters); + var it = try TestIterator.init(allocator, WPT_DIR, cmd.filters); defer it.deinit(); - var writer = try Writer.init(runner_arena, cmd.format); + var writer = try Writer.init(allocator, cmd.format); + defer writer.deinit(); // An arena for running each tests. Is reset after every test. var test_arena = ArenaAllocator.init(allocator); defer test_arena.deinit(); + var app = try App.init(allocator, .{ + .run_mode = .fetch, + }); + defer app.deinit(); + + var browser = try Browser.init(app); + defer browser.deinit(); + const session = try browser.newSession(); + while (try it.next()) |test_file| { - defer _ = test_arena.reset(.{ .retain_capacity = {} }); + defer _ = test_arena.reset(.retain_capacity); var err_out: ?[]const u8 = null; const result = run( test_arena.allocator(), + session, test_file, - &loader, &err_out, ) catch |err| blk: { if (err_out == null) { @@ -78,13 +92,6 @@ pub fn main() !void { } break :blk null; }; - - if (result == null and err_out == null) { - // We sometimes pass a non-test to `run` (we don't know it's a non - // test, we need to open the contents of the test file to find out - // and that's in run). - continue; - } try writer.process(test_file, result, err_out); } try writer.finalize(); @@ -92,102 +99,41 @@ pub fn main() !void { fn run( arena: Allocator, + session: *Session, test_file: []const u8, - loader: *FileLoader, err_out: *?[]const u8, -) !?[]const u8 { - // document - const html = blk: { - const full_path = try std.fs.path.join(arena, &.{ WPT_DIR, test_file }); - const file = try std.fs.cwd().openFile(full_path, .{}); - defer file.close(); - break :blk try file.readToEndAlloc(arena, 128 * 1024); - }; - - if (std.mem.indexOf(u8, html, "testharness.js") == null) { - // This isn't a test. A lot of files are helpers/content for tests to - // make use of. - return null; - } - - // this returns null for the success.html test in the root of tests/wpt - const dirname = std.fs.path.dirname(test_file) orelse ""; - - var runner = try @import("testing.zig").jsRunner(arena, .{ - .url = "http://127.0.0.1", - .html = html, - }); - defer runner.deinit(); - - defer if (err_out.*) |eo| { - // the error might be owned by the runner, we'll dupe it with our - // own arena so that it can be returned out of this function. - err_out.* = arena.dupe(u8, eo) catch "failed to dupe error"; - }; - - try polyfill.preload(arena, runner.page.main_context); - - // loop over the scripts. - const doc = parser.documentHTMLToDocument(runner.page.window.document); - const scripts = try parser.documentGetElementsByTagName(doc, "script"); - const script_count = try parser.nodeListLength(scripts); - for (0..script_count) |i| { - const s = (try parser.nodeListItem(scripts, @intCast(i))).?; - - // If the script contains an src attribute, load it. - if (try parser.elementGetAttribute(@as(*parser.Element, @ptrCast(s)), "src")) |src| { - var path = src; - if (!std.mem.startsWith(u8, src, "/")) { - path = try std.fs.path.join(arena, &.{ "/", dirname, path }); - } - const script_source = loader.get(path) catch |err| { - err_out.* = std.fmt.allocPrint(arena, "{s} - {s}", .{ @errorName(err), path }) catch null; - return err; - }; - try runner.exec(script_source, src, err_out); - } +) ![]const u8 { + const page = try session.createPage(); + defer session.removePage(); - // If the script as a source text, execute it. - const src = try parser.nodeTextContent(s) orelse continue; - try runner.exec(src, null, err_out); - } + const url = try std.fmt.allocPrint(arena, "http://localhost:9582/{s}", .{test_file}); + try page.navigate(url, .{}); - { - // Mark tests as ready to run. - const loadevt = try parser.eventCreate(); - defer parser.eventDestroy(loadevt); - - try parser.eventInit(loadevt, "load", .{}); - _ = try parser.eventTargetDispatchEvent( - parser.toEventTarget(@TypeOf(runner.page.window), &runner.page.window), - loadevt, - ); - } + page.wait(2); - { - // wait for all async executions - var try_catch: Env.TryCatch = undefined; - try_catch.init(runner.page.main_context); - defer try_catch.deinit(); - runner.page.wait(2); - - if (try_catch.hasCaught()) { - err_out.* = (try try_catch.err(arena)) orelse "unknwon error"; - } - } + const js_context = page.main_context; + var try_catch: Env.TryCatch = undefined; + try_catch.init(js_context); + defer try_catch.deinit(); // Check the final test status. - try runner.exec("report.status", "teststatus", err_out); + js_context.eval("report.status", "teststatus") catch |err| { + err_out.* = try_catch.err(arena) catch @errorName(err) orelse "unknown"; + return err; + }; // return the detailed result. - const res = try runner.eval("report.log", "report", err_out); + const value = js_context.exec("report.log", "report") catch |err| { + err_out.* = try_catch.err(arena) catch @errorName(err) orelse "unknown"; + return err; + }; - return try res.toString(arena); + return value.toString(arena); } const Writer = struct { format: Format, - arena: Allocator, + allocator: Allocator, pass_count: usize = 0, fail_count: usize = 0, case_pass_count: usize = 0, @@ -201,7 +147,7 @@ const Writer = struct { summary, }; - fn init(arena: Allocator, format: Format) !Writer { + fn init(allocator: Allocator, format: Format) !Writer { const out = std.fs.File.stdout(); var writer = out.writer(&.{}); @@ -210,12 +156,16 @@ const Writer = struct { } return .{ - .arena = arena, .format = format, .writer = writer, + .allocator = allocator, }; } + fn deinit(self: *Writer) void { + self.cases.deinit(self.allocator); + } + fn finalize(self: *Writer) !void { var writer = &self.writer.interface; if (self.format == .json) { @@ -303,7 +253,7 @@ const Writer = struct { case_fail_count += 1; } - try cases.append(self.arena, .{ + try cases.append(self.allocator, .{ .name = case_name, .pass = case_pass, .message = case_message, @@ -396,22 +346,26 @@ const TestIterator = struct { dir: Dir, walker: Dir.Walker, filters: [][]const u8, + read_arena: ArenaAllocator, const Dir = std.fs.Dir; - fn init(arena: Allocator, root: []const u8, filters: [][]const u8) !TestIterator { + fn init(allocator: Allocator, root: []const u8, filters: [][]const u8) !TestIterator { var dir = try std.fs.cwd().openDir(root, .{ .iterate = true, .no_follow = true }); errdefer dir.close(); return .{ .dir = dir, .filters = filters, - .walker = try dir.walk(arena), + .walker = try dir.walk(allocator), + .read_arena = ArenaAllocator.init(allocator), }; } fn deinit(self: *TestIterator) void { + self.walker.deinit(); self.dir.close(); + self.read_arena.deinit(); } fn next(self: *TestIterator) !?[]const u8 { @@ -436,6 +390,25 @@ const TestIterator = struct { } } + { + defer _ = self.read_arena.reset(.retain_capacity); + // We need to read the file's content to see if there's a + // "testharness.js" in it. If there isn't, it isn't a test. + // Shame we have to do this. + + const arena = self.read_arena.allocator(); + const full_path = try std.fs.path.join(arena, &.{ WPT_DIR, path }); + const file = try std.fs.cwd().openFile(full_path, .{}); + defer file.close(); + const html = try file.readToEndAlloc(arena, 128 * 1024); + + if (std.mem.indexOf(u8, html, "testharness.js") == null) { + // This isn't a test. A lot of files are helpers/content for tests to + // make use of. + continue :NEXT; + } + } + return path; } @@ -456,35 +429,16 @@ const Test = struct { cases: []Case, }; -pub const FileLoader = struct { - path: []const u8, - arena: Allocator, - files: std.StringHashMapUnmanaged([]const u8), +fn httpHandler(req: *std.http.Server.Request) !void { + const path = req.head.target; - pub fn init(arena: Allocator, path: []const u8) FileLoader { - return .{ - .path = path, - .files = .{}, - .arena = arena, - }; + if (std.mem.eql(u8, path, "/")) { + // There's 1 test that does an XHR request to this, and it just seems + // to want a 200 success. + return req.respond("Hello!", .{}); } - pub fn get(self: *FileLoader, name: []const u8) ![]const u8 { - const gop = try self.files.getOrPut(self.arena, name); - if (gop.found_existing == false) { - gop.key_ptr.* = try self.arena.dupe(u8, name); - gop.value_ptr.* = self.load(name) catch |err| { - _ = self.files.remove(name); - return err; - }; - } - return gop.value_ptr.*; - } - - fn load(self: *FileLoader, name: []const u8) ![]const u8 { - const filename = try std.fs.path.join(self.arena, &.{ self.path, name }); - var file = try std.fs.cwd().openFile(filename, .{}); - defer file.close(); - return file.readToEndAlloc(self.arena, 4 * 1024 * 1024); - } -}; + var buf: [1024]u8 = undefined; + const file_path = try std.fmt.bufPrint(&buf, WPT_DIR ++ "{s}", .{path}); + return TestHTTPServer.sendFile(req, file_path); +} diff --git a/src/runtime/js.zig b/src/runtime/js.zig index 134fa777a..ca16305e2 100644 --- a/src/runtime/js.zig +++ b/src/runtime/js.zig @@ -1516,12 +1516,12 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { } const op = js_obj.getInternalField(0).castTo(v8.External).get(); - const toa: *TaggedAnyOpaque = @ptrCast(@alignCast(op)); + const tao: *TaggedAnyOpaque = @ptrCast(@alignCast(op)); const expected_type_index = @field(TYPE_LOOKUP, type_name); - var type_index = toa.index; + var type_index = tao.index; if (type_index == expected_type_index) { - return @ptrCast(@alignCast(toa.ptr)); + return @ptrCast(@alignCast(tao.ptr)); } const meta_lookup = self.meta_lookup; @@ -1533,7 +1533,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { // ...unless, the proto is behind a pointer, then total_offset will // get reset to 0, and our base_ptr will move to the address // referenced by the proto field. - var base_ptr: usize = @intFromPtr(toa.ptr); + var base_ptr: usize = @intFromPtr(tao.ptr); // search through the prototype tree while (true) {