diff --git a/src/dom/document.zig b/src/dom/document.zig index f364b1bc..a2a74810 100644 --- a/src/dom/document.zig +++ b/src/dom/document.zig @@ -14,6 +14,7 @@ const Element = @import("element.zig").Element; const ElementUnion = @import("element.zig").Union; const DocumentType = @import("document_type.zig").DocumentType; +const DOMImplementation = @import("implementation.zig").DOMImplementation; // WEB IDL https://dom.spec.whatwg.org/#document pub const Document = struct { @@ -21,14 +22,17 @@ pub const Document = struct { pub const prototype = *Node; pub const mem_guarantied = true; - // pub fn constructor() *parser.Document { - // // TODO - // return .{}; - // } + pub fn constructor() *parser.Document { + return parser.domImplementationCreateHTMLDocument(null); + } // JS funcs // -------- - // + + pub fn get_implementation(_: *parser.Document) DOMImplementation { + return DOMImplementation{}; + } + pub fn get_documentElement(self: *parser.Document) ElementUnion { const e = parser.documentGetDocumentElement(self); return Element.toInterface(e); @@ -198,6 +202,21 @@ pub fn testExecFn( }; try checkCases(js_env, &getDocumentURI); + var getImplementation = [_]Case{ + .{ .src = "let impl = document.implementation", .ex = "undefined" }, + }; + try checkCases(js_env, &getImplementation); + + var new = [_]Case{ + .{ .src = "let d = new Document()", .ex = "undefined" }, + .{ .src = "d.characterSet", .ex = "UTF-8" }, + .{ .src = "d.URL", .ex = "about:blank" }, + .{ .src = "d.documentURI", .ex = "about:blank" }, + .{ .src = "d.compatMode", .ex = "CSS1Compat" }, + .{ .src = "d.contentType", .ex = "text/html" }, + }; + try checkCases(js_env, &new); + const tags = comptime parser.Tag.all(); comptime var createElements: [(tags.len) * 2]Case = undefined; inline for (tags, 0..) |tag, i| { diff --git a/src/dom/dom.zig b/src/dom/dom.zig index 9fb3ad79..6a29a937 100644 --- a/src/dom/dom.zig +++ b/src/dom/dom.zig @@ -2,11 +2,13 @@ const generate = @import("../generate.zig"); const DOMException = @import("exceptions.zig").DOMException; const EventTarget = @import("event_target.zig").EventTarget; +const DOMImplementation = @import("implementation.zig").DOMImplementation; const Nod = @import("node.zig"); pub const Interfaces = generate.Tuple(.{ DOMException, EventTarget, + DOMImplementation, Nod.Node, Nod.Interfaces, }); diff --git a/src/dom/implementation.zig b/src/dom/implementation.zig new file mode 100644 index 00000000..3e4e9eaa --- /dev/null +++ b/src/dom/implementation.zig @@ -0,0 +1,84 @@ +const std = @import("std"); + +const parser = @import("../netsurf.zig"); + +const jsruntime = @import("jsruntime"); +const Case = jsruntime.test_utils.Case; +const checkCases = jsruntime.test_utils.checkCases; + +const Document = @import("document.zig").Document; +const DocumentType = @import("document_type.zig").DocumentType; + +// WEB IDL https://dom.spec.whatwg.org/#domimplementation +pub const DOMImplementation = struct { + pub const mem_guarantied = true; + + pub fn _createDocumentType( + _: *DOMImplementation, + alloc: std.mem.Allocator, + qname: []const u8, + publicId: []const u8, + systemId: []const u8, + ) !*parser.DocumentType { + const cqname = try alloc.dupeZ(u8, qname); + defer alloc.free(cqname); + + const cpublicId = try alloc.dupeZ(u8, publicId); + defer alloc.free(cpublicId); + + const csystemId = try alloc.dupeZ(u8, systemId); + defer alloc.free(csystemId); + + return parser.domImplementationCreateDocumentType(cqname, cpublicId, csystemId); + } + + pub fn _createDocument( + _: *DOMImplementation, + alloc: std.mem.Allocator, + namespace: ?[]const u8, + qname: ?[]const u8, + doctype: ?*parser.DocumentType, + ) !*parser.Document { + var cnamespace: ?[:0]const u8 = null; + if (namespace) |ns| { + cnamespace = try alloc.dupeZ(u8, ns); + defer alloc.free(cnamespace.?); + } + + var cqname: ?[:0]const u8 = null; + if (qname) |qn| { + cqname = try alloc.dupeZ(u8, qn); + defer alloc.free(cqname.?); + } + + return parser.domImplementationCreateDocument(cnamespace, cqname, doctype); + } + + pub fn _createHTMLDocument(_: *DOMImplementation, title: ?[]const u8) *parser.Document { + return parser.domImplementationCreateHTMLDocument(title); + } + + pub fn _hasFeature(_: *DOMImplementation) bool { + return true; + } + + pub fn deinit(_: *DOMImplementation, _: std.mem.Allocator) void {} +}; + +// Tests +// ----- + +pub fn testExecFn( + _: std.mem.Allocator, + js_env: *jsruntime.Env, + comptime _: []jsruntime.API, +) !void { + var getImplementation = [_]Case{ + .{ .src = "let impl = document.implementation", .ex = "undefined" }, + .{ .src = "impl.createHTMLDocument();", .ex = "[object Document]" }, + .{ .src = "impl.createDocument(null, 'foo');", .ex = "[object Document]" }, + .{ .src = "impl.createDocumentType('foo', 'bar', 'baz')", .ex = "[object DocumentType]" }, + .{ .src = "impl.hasFeature()", .ex = "true" }, + }; + try checkCases(js_env, &getImplementation); +} diff --git a/src/netsurf.zig b/src/netsurf.zig index dbb3e61e..5a6bb796 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -826,6 +826,54 @@ pub inline fn documentTypeGetSystemId(dt: *DocumentType) []const u8 { return stringToData(s.?); } +// DOMImplementation +pub inline fn domImplementationCreateDocument(namespace: ?[:0]const u8, qname: ?[:0]const u8, doctype: ?*DocumentType) *Document { + var doc: ?*Document = undefined; + + var ptrnamespace: [*c]const u8 = null; + if (namespace) |ns| { + ptrnamespace = ns.ptr; + } + + var ptrqname: [*c]const u8 = null; + if (qname) |qn| { + ptrqname = qn.ptr; + } + + _ = c.dom_implementation_create_document( + c.DOM_IMPLEMENTATION_XML, + ptrnamespace, + ptrqname, + doctype, + null, + null, + &doc, + ); + return doc.?; +} + +pub inline fn domImplementationCreateDocumentType(qname: [:0]const u8, publicId: [:0]const u8, systemId: [:0]const u8) *DocumentType { + var dt: ?*DocumentType = undefined; + _ = c.dom_implementation_create_document_type(qname.ptr, publicId.ptr, systemId.ptr, &dt); + return dt.?; +} + +pub inline fn domImplementationCreateHTMLDocument(title: ?[]const u8) *Document { + var doc: ?*Document = undefined; + _ = c.dom_implementation_create_document( + c.DOM_IMPLEMENTATION_HTML, + null, + null, + null, + null, + null, + &doc, + ); + // TODO set title + _ = title; + return doc.?; +} + // Document pub const Document = c.dom_document; @@ -915,7 +963,7 @@ pub fn documentHTMLParseFromFile(filename: [:0]const u8) !*DocumentHTML { // The allocator is required to create a null terminated string. // The c string allocated is freed by the function. // The caller is responsible for closing the document. -pub fn documentHTMLParseFromStrAlloc(allocator: std.mem.Allocator, str: [:0]const u8) !*DocumentHTML { +pub fn documentHTMLParseFromStrAlloc(allocator: std.mem.Allocator, str: []const u8) !*DocumentHTML { // create a null terminated c string. const cstr = try allocator.dupeZ(u8, str); defer allocator.free(cstr); diff --git a/src/run_tests.zig b/src/run_tests.zig index 8acad71c..3a7dec64 100644 --- a/src/run_tests.zig +++ b/src/run_tests.zig @@ -13,6 +13,7 @@ const characterDataTestExecFn = @import("dom/character_data.zig").testExecFn; const textTestExecFn = @import("dom/text.zig").testExecFn; const HTMLCollectionTestExecFn = @import("dom/html_collection.zig").testExecFn; const DOMExceptionTestExecFn = @import("dom/exceptions.zig").testExecFn; +const DOMImplementationExecFn = @import("dom/implementation.zig").testExecFn; var doc: *parser.DocumentHTML = undefined; @@ -55,6 +56,7 @@ fn testsAllExecFn( textTestExecFn, HTMLCollectionTestExecFn, DOMExceptionTestExecFn, + DOMImplementationExecFn, }; inline for (testFns) |testFn| { diff --git a/tests/wpt/dom/nodes/DOMImplementation-createDocument-with-null-browsing-context-crash.html b/tests/wpt/dom/nodes/DOMImplementation-createDocument-with-null-browsing-context-crash.html new file mode 100644 index 00000000..c9393d0a --- /dev/null +++ b/tests/wpt/dom/nodes/DOMImplementation-createDocument-with-null-browsing-context-crash.html @@ -0,0 +1,13 @@ + + +DOMImplementation.createDocument() + + + + + + diff --git a/tests/wpt/dom/nodes/DOMImplementation-createDocument.html b/tests/wpt/dom/nodes/DOMImplementation-createDocument.html new file mode 100644 index 00000000..835002b4 --- /dev/null +++ b/tests/wpt/dom/nodes/DOMImplementation-createDocument.html @@ -0,0 +1,170 @@ + + +DOMImplementation.createDocument(namespace, qualifiedName, doctype) + + + + + + + + +
+ diff --git a/tests/wpt/dom/nodes/DOMImplementation-createDocumentType.html b/tests/wpt/dom/nodes/DOMImplementation-createDocumentType.html new file mode 100644 index 00000000..8d23e66a --- /dev/null +++ b/tests/wpt/dom/nodes/DOMImplementation-createDocumentType.html @@ -0,0 +1,123 @@ + + +DOMImplementation.createDocumentType(qualifiedName, publicId, systemId) + + + + + + + +
+ diff --git a/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument-with-null-browsing-context-crash.html b/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument-with-null-browsing-context-crash.html new file mode 100644 index 00000000..d0cd6f1f --- /dev/null +++ b/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument-with-null-browsing-context-crash.html @@ -0,0 +1,13 @@ + + +DOMImplementation.createHTMLDocument() + + + + + + diff --git a/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument-with-saved-implementation.html b/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument-with-saved-implementation.html new file mode 100644 index 00000000..bae22660 --- /dev/null +++ b/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument-with-saved-implementation.html @@ -0,0 +1,16 @@ + +DOMImplementation.createHTMLDocument + + + +
+ diff --git a/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument.html b/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument.html new file mode 100644 index 00000000..c6e0beaf --- /dev/null +++ b/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument.html @@ -0,0 +1,96 @@ + + + +DOMImplementation.createHTMLDocument + + + + + + + + +
+ diff --git a/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument.js b/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument.js new file mode 100644 index 00000000..3e7e9aa9 --- /dev/null +++ b/tests/wpt/dom/nodes/DOMImplementation-createHTMLDocument.js @@ -0,0 +1,25 @@ +function createHTMLDocuments(checkDoc) { + var tests = [ + ["", "", ""], + [null, "null", "null"], + [undefined, undefined, ""], + ["foo bar baz", "foo bar baz", "foo bar baz"], + ["foo\t\tbar baz", "foo\t\tbar baz", "foo bar baz"], + ["foo\n\nbar baz", "foo\n\nbar baz", "foo bar baz"], + ["foo\f\fbar baz", "foo\f\fbar baz", "foo bar baz"], + ["foo\r\rbar baz", "foo\r\rbar baz", "foo bar baz"], + ] + + tests.forEach(function(t, i) { + var title = t[0], expectedtitle = t[1], normalizedtitle = t[2] + test(function() { + var doc = document.implementation.createHTMLDocument(title); + checkDoc(doc, expectedtitle, normalizedtitle) + }, "createHTMLDocument test " + i + ": " + t.map(function(el) { return format_value(el) })) + }) + + test(function() { + var doc = document.implementation.createHTMLDocument(); + checkDoc(doc, undefined, "") + }, "Missing title argument"); +} diff --git a/tests/wpt/dom/nodes/DOMImplementation-hasFeature.html b/tests/wpt/dom/nodes/DOMImplementation-hasFeature.html new file mode 100644 index 00000000..637565a6 --- /dev/null +++ b/tests/wpt/dom/nodes/DOMImplementation-hasFeature.html @@ -0,0 +1,155 @@ + + +DOMImplementation.hasFeature(feature, version) + + + +
+