Merge pull request #216 from lightpanda-io/usrctx

Add user context
This commit is contained in:
Pierre Tachoire
2024-05-22 15:07:02 +02:00
committed by GitHub
19 changed files with 240 additions and 42 deletions

View File

@@ -39,3 +39,5 @@ pub const Interfaces = generate.Tuple(.{
Storage.Interfaces, Storage.Interfaces,
URL.Interfaces, URL.Interfaces,
}); });
pub const UserContext = @import("user_context.zig").UserContext;

View File

@@ -39,6 +39,9 @@ const storage = @import("../storage/storage.zig");
const FetchResult = std.http.Client.FetchResult; const FetchResult = std.http.Client.FetchResult;
const UserContext = @import("../user_context.zig").UserContext;
const HttpClient = @import("../async/Client.zig");
const log = std.log.scoped(.browser); const log = std.log.scoped(.browser);
// Browser is an instance of the browser. // Browser is an instance of the browser.
@@ -92,6 +95,7 @@ pub const Session = struct {
// TODO move the shed to the browser? // TODO move the shed to the browser?
storageShed: storage.Shed, storageShed: storage.Shed,
page: ?*Page = null, page: ?*Page = null,
httpClient: HttpClient,
jstypes: [Types.len]usize = undefined, jstypes: [Types.len]usize = undefined,
@@ -105,9 +109,11 @@ pub const Session = struct {
.loader = Loader.init(alloc), .loader = Loader.init(alloc),
.loop = try Loop.init(alloc), .loop = try Loop.init(alloc),
.storageShed = storage.Shed.init(alloc), .storageShed = storage.Shed.init(alloc),
.httpClient = undefined,
}; };
self.env = try Env.init(self.arena.allocator(), &self.loop); self.env = try Env.init(self.arena.allocator(), &self.loop, null);
self.httpClient = .{ .allocator = alloc, .loop = &self.loop };
try self.env.load(&self.jstypes); try self.env.load(&self.jstypes);
return self; return self;
@@ -122,6 +128,7 @@ pub const Session = struct {
self.loader.deinit(); self.loader.deinit();
self.loop.deinit(); self.loop.deinit();
self.storageShed.deinit(); self.storageShed.deinit();
self.httpClient.deinit();
self.alloc.destroy(self); self.alloc.destroy(self);
} }
@@ -289,6 +296,12 @@ pub const Page = struct {
log.debug("start js env", .{}); log.debug("start js env", .{});
try self.session.env.start(alloc); try self.session.env.start(alloc);
// replace the user context document with the new one.
try self.session.env.setUserContext(.{
.document = html_doc,
.httpClient = &self.session.httpClient,
});
// add global objects // add global objects
log.debug("setup global env", .{}); log.debug("setup global env", .{});
try self.session.env.bindGlobal(&self.session.window); try self.session.env.bindGlobal(&self.session.window);

View File

@@ -15,13 +15,45 @@
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const parser = @import("../netsurf.zig"); const parser = @import("../netsurf.zig");
const jsruntime = @import("jsruntime");
const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases;
const CharacterData = @import("character_data.zig").CharacterData; const CharacterData = @import("character_data.zig").CharacterData;
const UserContext = @import("../user_context.zig").UserContext;
// https://dom.spec.whatwg.org/#interface-comment
pub const Comment = struct { pub const Comment = struct {
pub const Self = parser.Comment; pub const Self = parser.Comment;
pub const prototype = *CharacterData; pub const prototype = *CharacterData;
pub const mem_guarantied = true; pub const mem_guarantied = true;
pub fn constructor(userctx: UserContext, data: ?[]const u8) !*parser.Comment {
return parser.documentCreateComment(
parser.documentHTMLToDocument(userctx.document),
data orelse "",
);
}
}; };
// Tests
// -----
pub fn testExecFn(
_: std.mem.Allocator,
js_env: *jsruntime.Env,
) anyerror!void {
var constructor = [_]Case{
.{ .src = "let comment = new Comment('foo')", .ex = "undefined" },
.{ .src = "comment.data", .ex = "foo" },
.{ .src = "let emptycomment = new Comment()", .ex = "undefined" },
.{ .src = "emptycomment.data", .ex = "" },
};
try checkCases(js_env, &constructor);
}

View File

@@ -40,14 +40,26 @@ const DocumentType = @import("document_type.zig").DocumentType;
const DocumentFragment = @import("document_fragment.zig").DocumentFragment; const DocumentFragment = @import("document_fragment.zig").DocumentFragment;
const DOMImplementation = @import("implementation.zig").DOMImplementation; const DOMImplementation = @import("implementation.zig").DOMImplementation;
const UserContext = @import("../user_context.zig").UserContext;
// WEB IDL https://dom.spec.whatwg.org/#document // WEB IDL https://dom.spec.whatwg.org/#document
pub const Document = struct { pub const Document = struct {
pub const Self = parser.Document; pub const Self = parser.Document;
pub const prototype = *Node; pub const prototype = *Node;
pub const mem_guarantied = true; pub const mem_guarantied = true;
pub fn constructor() !*parser.Document { pub fn constructor(userctx: UserContext) !*parser.DocumentHTML {
return try parser.domImplementationCreateHTMLDocument(null); const doc = try parser.documentCreateDocument(
try parser.documentHTMLGetTitle(userctx.document),
);
// we have to work w/ document instead of html document.
const ddoc = parser.documentHTMLToDocument(doc);
const ccur = parser.documentHTMLToDocument(userctx.document);
try parser.documentSetDocumentURI(ddoc, try parser.documentGetDocumentURI(ccur));
try parser.documentSetInputEncoding(ddoc, try parser.documentGetInputEncoding(ccur));
return doc;
} }
// JS funcs // JS funcs
@@ -262,6 +274,13 @@ pub fn testExecFn(
.{ .src = "newdoc.children.length", .ex = "0" }, .{ .src = "newdoc.children.length", .ex = "0" },
.{ .src = "newdoc.getElementsByTagName('*').length", .ex = "0" }, .{ .src = "newdoc.getElementsByTagName('*').length", .ex = "0" },
.{ .src = "newdoc.getElementsByTagName('*').item(0)", .ex = "null" }, .{ .src = "newdoc.getElementsByTagName('*').item(0)", .ex = "null" },
.{ .src = "newdoc.inputEncoding === document.inputEncoding", .ex = "true" },
.{ .src = "newdoc.documentURI === document.documentURI", .ex = "true" },
.{ .src = "newdoc.URL === document.URL", .ex = "true" },
.{ .src = "newdoc.compatMode === document.compatMode", .ex = "true" },
.{ .src = "newdoc.characterSet === document.characterSet", .ex = "true" },
.{ .src = "newdoc.charset === document.charset", .ex = "true" },
.{ .src = "newdoc.contentType === document.contentType", .ex = "true" },
}; };
try checkCases(js_env, &constructor); try checkCases(js_env, &constructor);

View File

@@ -20,20 +20,37 @@ const std = @import("std");
const parser = @import("../netsurf.zig"); const parser = @import("../netsurf.zig");
const jsruntime = @import("jsruntime");
const Case = jsruntime.test_utils.Case;
const checkCases = jsruntime.test_utils.checkCases;
const Node = @import("node.zig").Node; const Node = @import("node.zig").Node;
const UserContext = @import("../user_context.zig").UserContext;
// WEB IDL https://dom.spec.whatwg.org/#documentfragment // WEB IDL https://dom.spec.whatwg.org/#documentfragment
pub const DocumentFragment = struct { pub const DocumentFragment = struct {
pub const Self = parser.DocumentFragment; pub const Self = parser.DocumentFragment;
pub const prototype = *Node; pub const prototype = *Node;
pub const mem_guarantied = true; pub const mem_guarantied = true;
// TODO add constructor, but I need to associate the new DocumentFragment pub fn constructor(userctx: UserContext) !*parser.DocumentFragment {
// with the current document global object... return parser.documentCreateDocumentFragment(
// > The new DocumentFragment() constructor steps are to set thiss node parser.documentHTMLToDocument(userctx.document),
// > document to current global objects associated Document. );
// https://dom.spec.whatwg.org/#dom-documentfragment-documentfragment
pub fn constructor() !*parser.DocumentFragment {
return error.NotImplemented;
} }
}; };
// Tests
// -----
pub fn testExecFn(
_: std.mem.Allocator,
js_env: *jsruntime.Env,
) anyerror!void {
var constructor = [_]Case{
.{ .src = "const dc = new DocumentFragment()", .ex = "undefined" },
.{ .src = "dc.constructor.name", .ex = "DocumentFragment" },
};
try checkCases(js_env, &constructor);
}

View File

@@ -75,7 +75,7 @@ pub const DOMImplementation = struct {
return try parser.domImplementationCreateDocument(cnamespace, cqname, doctype); return try parser.domImplementationCreateDocument(cnamespace, cqname, doctype);
} }
pub fn _createHTMLDocument(_: *DOMImplementation, title: ?[]const u8) !*parser.Document { pub fn _createHTMLDocument(_: *DOMImplementation, title: ?[]const u8) !*parser.DocumentHTML {
return try parser.domImplementationCreateHTMLDocument(title); return try parser.domImplementationCreateHTMLDocument(title);
} }
@@ -95,7 +95,8 @@ pub fn testExecFn(
) anyerror!void { ) anyerror!void {
var getImplementation = [_]Case{ var getImplementation = [_]Case{
.{ .src = "let impl = document.implementation", .ex = "undefined" }, .{ .src = "let impl = document.implementation", .ex = "undefined" },
.{ .src = "impl.createHTMLDocument();", .ex = "[object Document]" }, .{ .src = "impl.createHTMLDocument();", .ex = "[object HTMLDocument]" },
.{ .src = "impl.createHTMLDocument('foo');", .ex = "[object HTMLDocument]" },
.{ .src = "impl.createDocument(null, 'foo');", .ex = "[object Document]" }, .{ .src = "impl.createDocument(null, 'foo');", .ex = "[object Document]" },
.{ .src = "impl.createDocumentType('foo', 'bar', 'baz')", .ex = "[object DocumentType]" }, .{ .src = "impl.createDocumentType('foo', 'bar', 'baz')", .ex = "[object DocumentType]" },
.{ .src = "impl.hasFeature()", .ex = "true" }, .{ .src = "impl.hasFeature()", .ex = "true" },

View File

@@ -277,14 +277,30 @@ pub const Node = struct {
return try Node.toInterface(res); return try Node.toInterface(res);
} }
// Check if the hierarchy node tree constraints are respected.
// For now, it checks only if new nodes are not self.
// TODO implements the others contraints.
// see https://dom.spec.whatwg.org/#concept-node-tree
pub fn hierarchy(self: *parser.Node, nodes: ?Variadic(*parser.Node)) !bool {
if (nodes == null) return true;
if (nodes.?.slice.len == 0) return true;
for (nodes.?.slice) |node| if (self == node) return false;
return true;
}
// TODO according with https://dom.spec.whatwg.org/#parentnode, the // TODO according with https://dom.spec.whatwg.org/#parentnode, the
// function must accept either node or string. // function must accept either node or string.
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114 // blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
pub fn prepend(self: *parser.Node, nodes: ?Variadic(*parser.Node)) !void { pub fn prepend(self: *parser.Node, nodes: ?Variadic(*parser.Node)) !void {
if (nodes == null) return; if (nodes == null) return;
if (nodes.?.slice.len == 0) return; if (nodes.?.slice.len == 0) return;
const first = try parser.nodeFirstChild(self);
// check hierarchy
if (!try hierarchy(self, nodes)) return parser.DOMError.HierarchyRequest;
const first = try parser.nodeFirstChild(self);
if (first == null) { if (first == null) {
for (nodes.?.slice) |node| { for (nodes.?.slice) |node| {
_ = try parser.nodeAppendChild(self, node); _ = try parser.nodeAppendChild(self, node);
@@ -303,6 +319,10 @@ pub const Node = struct {
pub fn append(self: *parser.Node, nodes: ?Variadic(*parser.Node)) !void { pub fn append(self: *parser.Node, nodes: ?Variadic(*parser.Node)) !void {
if (nodes == null) return; if (nodes == null) return;
if (nodes.?.slice.len == 0) return; if (nodes.?.slice.len == 0) return;
// check hierarchy
if (!try hierarchy(self, nodes)) return parser.DOMError.HierarchyRequest;
for (nodes.?.slice) |node| { for (nodes.?.slice) |node| {
_ = try parser.nodeAppendChild(self, node); _ = try parser.nodeAppendChild(self, node);
} }
@@ -312,12 +332,15 @@ pub const Node = struct {
// function must accept either node or string. // function must accept either node or string.
// blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114 // blocked by https://github.com/lightpanda-io/jsruntime-lib/issues/114
pub fn replaceChildren(self: *parser.Node, nodes: ?Variadic(*parser.Node)) !void { pub fn replaceChildren(self: *parser.Node, nodes: ?Variadic(*parser.Node)) !void {
// remove existing children
try removeChildren(self);
if (nodes == null) return; if (nodes == null) return;
if (nodes.?.slice.len == 0) return; if (nodes.?.slice.len == 0) return;
// check hierarchy
if (!try hierarchy(self, nodes)) return parser.DOMError.HierarchyRequest;
// remove existing children
try removeChildren(self);
// add new children // add new children
for (nodes.?.slice) |node| { for (nodes.?.slice) |node| {
_ = try parser.nodeAppendChild(self, node); _ = try parser.nodeAppendChild(self, node);

View File

@@ -28,6 +28,8 @@ const parser = @import("../netsurf.zig");
const CharacterData = @import("character_data.zig").CharacterData; const CharacterData = @import("character_data.zig").CharacterData;
const CDATASection = @import("cdata_section.zig").CDATASection; const CDATASection = @import("cdata_section.zig").CDATASection;
const UserContext = @import("../user_context.zig").UserContext;
// Text interfaces // Text interfaces
pub const Interfaces = generate.Tuple(.{ pub const Interfaces = generate.Tuple(.{
CDATASection, CDATASection,
@@ -38,6 +40,13 @@ pub const Text = struct {
pub const prototype = *CharacterData; pub const prototype = *CharacterData;
pub const mem_guarantied = true; pub const mem_guarantied = true;
pub fn constructor(userctx: UserContext, data: ?[]const u8) !*parser.Text {
return parser.documentCreateTextNode(
parser.documentHTMLToDocument(userctx.document),
data orelse "",
);
}
// JS funcs // JS funcs
// -------- // --------
@@ -62,6 +71,15 @@ pub fn testExecFn(
_: std.mem.Allocator, _: std.mem.Allocator,
js_env: *jsruntime.Env, js_env: *jsruntime.Env,
) anyerror!void { ) anyerror!void {
var constructor = [_]Case{
.{ .src = "let t = new Text('foo')", .ex = "undefined" },
.{ .src = "t.data", .ex = "foo" },
.{ .src = "let emptyt = new Text()", .ex = "undefined" },
.{ .src = "emptyt.data", .ex = "" },
};
try checkCases(js_env, &constructor);
var get_whole_text = [_]Case{ var get_whole_text = [_]Case{
.{ .src = "let text = document.getElementById('link').firstChild", .ex = "undefined" }, .{ .src = "let text = document.getElementById('link').firstChild", .ex = "undefined" },
.{ .src = "text.wholeText === 'OK'", .ex = "true" }, .{ .src = "text.wholeText === 'OK'", .ex = "true" },

View File

@@ -25,6 +25,7 @@ const apiweb = @import("apiweb.zig");
const Window = @import("html/window.zig").Window; const Window = @import("html/window.zig").Window;
pub const Types = jsruntime.reflect(apiweb.Interfaces); pub const Types = jsruntime.reflect(apiweb.Interfaces);
pub const UserContext = apiweb.UserContext;
const socket_path = "/tmp/browsercore-server.sock"; const socket_path = "/tmp/browsercore-server.sock";
@@ -103,5 +104,5 @@ pub fn main() !void {
try server.listen(addr); try server.listen(addr);
std.debug.print("Listening on: {s}...\n", .{socket_path}); std.debug.print("Listening on: {s}...\n", .{socket_path});
try jsruntime.loadEnv(&arena, execJS); try jsruntime.loadEnv(&arena, null, execJS);
} }

View File

@@ -23,6 +23,7 @@ const jsruntime = @import("jsruntime");
const apiweb = @import("apiweb.zig"); const apiweb = @import("apiweb.zig");
pub const Types = jsruntime.reflect(apiweb.Interfaces); pub const Types = jsruntime.reflect(apiweb.Interfaces);
pub const UserContext = apiweb.UserContext;
pub const std_options = struct { pub const std_options = struct {
pub const log_level = .debug; pub const log_level = .debug;

View File

@@ -28,6 +28,8 @@ const storage = @import("storage/storage.zig");
const html_test = @import("html_test.zig").html; const html_test = @import("html_test.zig").html;
pub const Types = jsruntime.reflect(apiweb.Interfaces); pub const Types = jsruntime.reflect(apiweb.Interfaces);
pub const UserContext = apiweb.UserContext;
const Client = @import("async/Client.zig");
var doc: *parser.DocumentHTML = undefined; var doc: *parser.DocumentHTML = undefined;
@@ -39,6 +41,14 @@ fn execJS(
try js_env.start(alloc); try js_env.start(alloc);
defer js_env.stop(); defer js_env.stop();
var cli = Client{ .allocator = alloc, .loop = js_env.nat_ctx.loop };
defer cli.deinit();
try js_env.setUserContext(UserContext{
.document = doc,
.httpClient = &cli,
});
var storageShelf = storage.Shelf.init(alloc); var storageShelf = storage.Shelf.init(alloc);
defer storageShelf.deinit(); defer storageShelf.deinit();

View File

@@ -49,6 +49,7 @@ const Out = enum {
pub const Types = jsruntime.reflect(apiweb.Interfaces); pub const Types = jsruntime.reflect(apiweb.Interfaces);
pub const GlobalType = apiweb.GlobalType; pub const GlobalType = apiweb.GlobalType;
pub const UserContext = apiweb.UserContext;
// TODO For now the WPT tests run is specific to 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 // It manually load js framwork libs, and run the first script w/ js content in

View File

@@ -1763,21 +1763,29 @@ pub inline fn domImplementationCreateDocumentType(
return dt.?; return dt.?;
} }
pub inline fn domImplementationCreateHTMLDocument(title: ?[]const u8) !*Document { pub inline fn domImplementationCreateHTMLDocument(title: ?[]const u8) !*DocumentHTML {
var doc: ?*Document = undefined; const doc_html = try documentCreateDocument(title);
const err = c.dom_implementation_create_document( const doc = documentHTMLToDocument(doc_html);
c.DOM_IMPLEMENTATION_HTML,
null, // add hierarchy: html, head, body.
null, const html = try documentCreateElement(doc, "html");
null, _ = try nodeAppendChild(documentToNode(doc), elementToNode(html));
null,
null, const head = try documentCreateElement(doc, "head");
&doc, _ = try nodeAppendChild(elementToNode(html), elementToNode(head));
);
try DOMErr(err); if (title) |t| {
// TODO set title try documentHTMLSetTitle(doc_html, t);
_ = title; const htitle = try documentCreateElement(doc, "title");
return doc.?; const txt = try documentCreateTextNode(doc, t);
_ = try nodeAppendChild(elementToNode(htitle), @as(*Node, @ptrCast(txt)));
_ = try nodeAppendChild(elementToNode(head), elementToNode(htitle));
}
const body = try documentCreateElement(doc, "body");
_ = try nodeAppendChild(elementToNode(html), elementToNode(body));
return doc_html;
} }
// Document // Document
@@ -1833,6 +1841,28 @@ pub inline fn documentGetInputEncoding(doc: *Document) ![]const u8 {
return strToData(s.?); return strToData(s.?);
} }
pub inline fn documentSetInputEncoding(doc: *Document, enc: []const u8) !void {
const err = documentVtable(doc).dom_document_set_input_encoding.?(doc, try strFromData(enc));
try DOMErr(err);
}
pub inline fn documentCreateDocument(title: ?[]const u8) !*DocumentHTML {
var doc: ?*Document = undefined;
const err = c.dom_implementation_create_document(
c.DOM_IMPLEMENTATION_HTML,
null,
null,
null,
null,
null,
&doc,
);
try DOMErr(err);
const doc_html = @as(*DocumentHTML, @ptrCast(doc.?));
if (title) |t| try documentHTMLSetTitle(doc_html, t);
return doc_html;
}
pub inline fn documentCreateElement(doc: *Document, tag_name: []const u8) !*Element { pub inline fn documentCreateElement(doc: *Document, tag_name: []const u8) !*Element {
var elem: ?*Element = undefined; var elem: ?*Element = undefined;
const err = documentVtable(doc).dom_document_create_element.?(doc, try strFromData(tag_name), &elem); const err = documentVtable(doc).dom_document_create_element.?(doc, try strFromData(tag_name), &elem);

View File

@@ -30,6 +30,7 @@ const xhr = @import("xhr/xhr.zig");
const storage = @import("storage/storage.zig"); const storage = @import("storage/storage.zig");
const url = @import("url/url.zig"); const url = @import("url/url.zig");
const urlquery = @import("url/query.zig"); const urlquery = @import("url/query.zig");
const Client = @import("async/Client.zig");
const documentTestExecFn = @import("dom/document.zig").testExecFn; const documentTestExecFn = @import("dom/document.zig").testExecFn;
const HTMLDocumentTestExecFn = @import("html/document.zig").testExecFn; const HTMLDocumentTestExecFn = @import("html/document.zig").testExecFn;
@@ -46,6 +47,8 @@ const NodeListTestExecFn = @import("dom/nodelist.zig").testExecFn;
const AttrTestExecFn = @import("dom/attribute.zig").testExecFn; const AttrTestExecFn = @import("dom/attribute.zig").testExecFn;
const EventTargetTestExecFn = @import("dom/event_target.zig").testExecFn; const EventTargetTestExecFn = @import("dom/event_target.zig").testExecFn;
const ProcessingInstructionTestExecFn = @import("dom/processing_instruction.zig").testExecFn; const ProcessingInstructionTestExecFn = @import("dom/processing_instruction.zig").testExecFn;
const CommentTestExecFn = @import("dom/comment.zig").testExecFn;
const DocumentFragmentTestExecFn = @import("dom/document_fragment.zig").testExecFn;
const EventTestExecFn = @import("events/event.zig").testExecFn; const EventTestExecFn = @import("events/event.zig").testExecFn;
const XHRTestExecFn = xhr.testExecFn; const XHRTestExecFn = xhr.testExecFn;
const ProgressEventTestExecFn = @import("xhr/progress_event.zig").testExecFn; const ProgressEventTestExecFn = @import("xhr/progress_event.zig").testExecFn;
@@ -54,6 +57,7 @@ const URLTestExecFn = url.testExecFn;
const HTMLElementTestExecFn = @import("html/elements.zig").testExecFn; const HTMLElementTestExecFn = @import("html/elements.zig").testExecFn;
pub const Types = jsruntime.reflect(apiweb.Interfaces); pub const Types = jsruntime.reflect(apiweb.Interfaces);
pub const UserContext = @import("user_context.zig").UserContext;
var doc: *parser.DocumentHTML = undefined; var doc: *parser.DocumentHTML = undefined;
@@ -81,6 +85,14 @@ fn testExecFn(
std.debug.print("documentHTMLClose error: {s}\n", .{@errorName(err)}); std.debug.print("documentHTMLClose error: {s}\n", .{@errorName(err)});
}; };
var cli = Client{ .allocator = alloc, .loop = js_env.nat_ctx.loop };
defer cli.deinit();
try js_env.setUserContext(.{
.document = doc,
.httpClient = &cli,
});
// alias global as self and window // alias global as self and window
var window = Window.create(null); var window = Window.create(null);
@@ -111,6 +123,8 @@ fn testsAllExecFn(
DOMTokenListExecFn, DOMTokenListExecFn,
NodeListTestExecFn, NodeListTestExecFn,
AttrTestExecFn, AttrTestExecFn,
CommentTestExecFn,
DocumentFragmentTestExecFn,
EventTargetTestExecFn, EventTargetTestExecFn,
EventTestExecFn, EventTestExecFn,
XHRTestExecFn, XHRTestExecFn,
@@ -315,7 +329,7 @@ fn testJSRuntime(alloc: std.mem.Allocator) !void {
var arena_alloc = std.heap.ArenaAllocator.init(alloc); var arena_alloc = std.heap.ArenaAllocator.init(alloc);
defer arena_alloc.deinit(); defer arena_alloc.deinit();
try jsruntime.loadEnv(&arena_alloc, testsAllExecFn); try jsruntime.loadEnv(&arena_alloc, null, testsAllExecFn);
} }
test "DocumentHTMLParseFromStr" { test "DocumentHTMLParseFromStr" {

View File

@@ -21,6 +21,7 @@ const std = @import("std");
const tests = @import("run_tests.zig"); const tests = @import("run_tests.zig");
pub const Types = tests.Types; pub const Types = tests.Types;
pub const UserContext = tests.UserContext;
pub fn main() !void { pub fn main() !void {
try tests.main(); try tests.main();

8
src/user_context.zig Normal file
View File

@@ -0,0 +1,8 @@
const std = @import("std");
const parser = @import("netsurf.zig");
const Client = @import("async/Client.zig");
pub const UserContext = struct {
document: *parser.DocumentHTML,
httpClient: *Client,
};

View File

@@ -30,6 +30,8 @@ const Window = @import("../html/window.zig").Window;
const storage = @import("../storage/storage.zig"); const storage = @import("../storage/storage.zig");
const Types = @import("../main_wpt.zig").Types; const Types = @import("../main_wpt.zig").Types;
const UserContext = @import("../main_wpt.zig").UserContext;
const Client = @import("../async/Client.zig");
// runWPT parses the given HTML file, starts a js env and run the first script // runWPT parses the given HTML file, starts a js env and run the first script
// tags containing javascript sources. // tags containing javascript sources.
@@ -50,7 +52,14 @@ pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const
// create JS env // create JS env
var loop = try Loop.init(alloc); var loop = try Loop.init(alloc);
defer loop.deinit(); defer loop.deinit();
var js_env = try Env.init(alloc, &loop);
var cli = Client{ .allocator = alloc, .loop = &loop };
defer cli.deinit();
var js_env = try Env.init(alloc, &loop, UserContext{
.document = html_doc,
.httpClient = &cli,
});
defer js_env.deinit(); defer js_env.deinit();
var storageShelf = storage.Shelf.init(alloc); var storageShelf = storage.Shelf.init(alloc);

View File

@@ -37,6 +37,8 @@ const Client = @import("../async/Client.zig");
const parser = @import("../netsurf.zig"); const parser = @import("../netsurf.zig");
const UserContext = @import("../user_context.zig").UserContext;
const log = std.log.scoped(.xhr); const log = std.log.scoped(.xhr);
// XHR interfaces // XHR interfaces
@@ -149,7 +151,7 @@ pub const XMLHttpRequest = struct {
proto: XMLHttpRequestEventTarget = XMLHttpRequestEventTarget{}, proto: XMLHttpRequestEventTarget = XMLHttpRequestEventTarget{},
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
cli: Client, cli: *Client,
impl: YieldImpl, impl: YieldImpl,
priv_state: PrivState = .new, priv_state: PrivState = .new,
@@ -185,7 +187,7 @@ pub const XMLHttpRequest = struct {
const min_delay: u64 = 50000000; // 50ms const min_delay: u64 = 50000000; // 50ms
pub fn constructor(alloc: std.mem.Allocator, loop: *Loop) !XMLHttpRequest { pub fn constructor(alloc: std.mem.Allocator, loop: *Loop, userctx: UserContext) !XMLHttpRequest {
return .{ return .{
.alloc = alloc, .alloc = alloc,
.headers = .{ .allocator = alloc, .owned = true }, .headers = .{ .allocator = alloc, .owned = true },
@@ -195,8 +197,7 @@ pub const XMLHttpRequest = struct {
.url = null, .url = null,
.uri = undefined, .uri = undefined,
.state = UNSENT, .state = UNSENT,
// TODO retrieve the HTTP client globally to reuse existing connections. .cli = userctx.httpClient,
.cli = .{ .allocator = alloc, .loop = loop },
}; };
} }
@@ -235,9 +236,6 @@ pub const XMLHttpRequest = struct {
self.response_headers.deinit(); self.response_headers.deinit();
self.proto.deinit(alloc); self.proto.deinit(alloc);
// TODO the client must be shared between requests.
self.cli.deinit();
} }
pub fn get_readyState(self: *XMLHttpRequest) u16 { pub fn get_readyState(self: *XMLHttpRequest) u16 {