Skip to content

Commit 6742646

Browse files
committed
DOMParser
1 parent 6cf0163 commit 6742646

File tree

4 files changed

+184
-2
lines changed

4 files changed

+184
-2
lines changed

src/browser/js/bridge.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ pub const JsApis = flattenTypes(&.{
489489
@import("../webapi/DOMTreeWalker.zig"),
490490
@import("../webapi/DOMNodeIterator.zig"),
491491
@import("../webapi/DOMRect.zig"),
492+
@import("../webapi/DOMParser.zig"),
492493
@import("../webapi/NodeFilter.zig"),
493494
@import("../webapi/Element.zig"),
494495
@import("../webapi/element/DOMStringMap.zig"),

src/browser/tests/domparser.html

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<!DOCTYPE html>
2+
<script src="testing.js"></script>
3+
4+
<!-- <script id=basic>
5+
{
6+
const parser = new DOMParser();
7+
testing.expectEqual('object', typeof parser);
8+
testing.expectEqual('function', typeof parser.parseFromString);
9+
}
10+
</script> -->
11+
12+
<script id=parseSimpleHTML>
13+
{
14+
const parser = new DOMParser();
15+
const doc = parser.parseFromString('<div>Hello World</div>', 'text/html');
16+
17+
testing.expectEqual('object', typeof doc);
18+
testing.expectEqual('[object HTMLDocument]', doc.toString());
19+
20+
const div = doc.querySelector('div');
21+
testing.expectEqual('DIV', div.tagName);
22+
testing.expectEqual('Hello World', div.textContent);
23+
}
24+
</script>
25+
26+
<!-- <script id=parseWithAttributes>
27+
{
28+
const parser = new DOMParser();
29+
const doc = parser.parseFromString('<div id="test" class="foo">Content</div>', 'text/html');
30+
31+
const div = doc.querySelector('div');
32+
testing.expectEqual('test', div.id);
33+
testing.expectEqual('foo', div.className);
34+
testing.expectEqual('Content', div.textContent);
35+
}
36+
</script>
37+
38+
<script id=parseMultipleElements>
39+
{
40+
const parser = new DOMParser();
41+
const doc = parser.parseFromString('<div>First</div><span>Second</span>', 'text/html');
42+
43+
const div = doc.querySelector('div');
44+
const span = doc.querySelector('span');
45+
46+
testing.expectEqual('DIV', div.tagName);
47+
testing.expectEqual('First', div.textContent);
48+
testing.expectEqual('SPAN', span.tagName);
49+
testing.expectEqual('Second', span.textContent);
50+
}
51+
</script>
52+
53+
<script id=parseNestedElements>
54+
{
55+
const parser = new DOMParser();
56+
const doc = parser.parseFromString('<div><p><span>Nested</span></p></div>', 'text/html');
57+
58+
const div = doc.querySelector('div');
59+
const p = doc.querySelector('p');
60+
const span = doc.querySelector('span');
61+
62+
testing.expectEqual('DIV', div.tagName);
63+
testing.expectEqual('P', p.tagName);
64+
testing.expectEqual('SPAN', span.tagName);
65+
testing.expectEqual('Nested', span.textContent);
66+
testing.expectEqual(p, div.firstChild);
67+
testing.expectEqual(span, p.firstChild);
68+
}
69+
</script>
70+
71+
<script id=parseEmptyString>
72+
{
73+
const parser = new DOMParser();
74+
const doc = parser.parseFromString('', 'text/html');
75+
76+
testing.expectEqual('object', typeof doc);
77+
testing.expectEqual('[object HTMLDocument]', doc.toString());
78+
}
79+
</script>
80+
81+
<script id=parsedDocumentIsIndependent>
82+
{
83+
const parser = new DOMParser();
84+
const doc = parser.parseFromString('<div id="parsed">Parsed</div>', 'text/html');
85+
86+
// The parsed document should be independent from the current document
87+
const currentDiv = document.querySelector('div');
88+
const parsedDiv = doc.querySelector('div');
89+
90+
testing.expectEqual(null, currentDiv);
91+
testing.expectEqual('parsed', parsedDiv.id);
92+
testing.expectEqual('Parsed', parsedDiv.textContent);
93+
}
94+
</script>
95+
96+
<script id=malformedHTMLDoesNotThrow>
97+
{
98+
const parser = new DOMParser();
99+
100+
// HTML parsers should be forgiving and not throw on malformed HTML
101+
const doc1 = parser.parseFromString('<div><p>unclosed', 'text/html');
102+
testing.expectEqual('object', typeof doc1);
103+
104+
const doc2 = parser.parseFromString('<<<invalid>>>', 'text/html');
105+
testing.expectEqual('object', typeof doc2);
106+
}
107+
</script>
108+
109+
<script id=unsupportedMimeType>
110+
{
111+
const parser = new DOMParser();
112+
113+
// Should throw an error for unsupported MIME types
114+
testing.withError((err) => {
115+
testing.expectEqual('NotSupported', err.message);
116+
}, () => {
117+
parser.parseFromString('<div>test</div>', 'application/xml');
118+
});
119+
}
120+
</script>
121+
-->

src/browser/webapi/DOMParser.zig

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
const std = @import("std");
2+
3+
const js = @import("../js/js.zig");
4+
const Page = @import("../Page.zig");
5+
const Document = @import("Document.zig");
6+
const HTMLDocument = @import("HTMLDocument.zig");
7+
8+
const DOMParser = @This();
9+
// @ZIGDOM support empty structs
10+
_: u8 = 0,
11+
12+
pub fn init() DOMParser {
13+
return .{};
14+
}
15+
16+
pub fn parseFromString(self: *const DOMParser, html: []const u8, mime_type: []const u8, page: *Page) !*HTMLDocument {
17+
_ = self;
18+
19+
// For now, only support text/html
20+
if (!std.mem.eql(u8, mime_type, "text/html")) {
21+
return error.NotSupported;
22+
}
23+
24+
// Create a new HTMLDocument
25+
const doc = try page._factory.document(HTMLDocument{
26+
._proto = undefined,
27+
});
28+
29+
// Parse HTML into the document
30+
const Parser = @import("../parser/Parser.zig");
31+
var parser = Parser.init(page.arena, doc.asNode(), page);
32+
parser.parse(html);
33+
34+
if (parser.err) |pe| {
35+
return pe.err;
36+
}
37+
38+
return doc;
39+
}
40+
41+
pub const JsApi = struct {
42+
pub const bridge = js.Bridge(DOMParser);
43+
44+
pub const Meta = struct {
45+
pub const name = "DOMParser";
46+
pub const prototype_chain = bridge.prototypeChain();
47+
pub var class_id: bridge.ClassId = undefined;
48+
};
49+
50+
pub const constructor = bridge.constructor(DOMParser.init, .{});
51+
pub const parseFromString = bridge.function(DOMParser.parseFromString, .{});
52+
};
53+
54+
const testing = @import("../../testing.zig");
55+
test "WebApi: DOMParser" {
56+
try testing.htmlRunner("domparser.html", .{});
57+
}

src/browser/webapi/Document.zig

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,11 @@ pub fn querySelectorAll(self: *Document, input: []const u8, page: *Page) !*Selec
122122
return Selector.querySelectorAll(self.asNode(), input, page);
123123
}
124124

125-
pub fn className(_: *const Document) []const u8 {
126-
return "[object Document]";
125+
pub fn className(self: *const Document) []const u8 {
126+
return switch (self._type) {
127+
.generic => "[object Document]",
128+
.html => "[object HTMLDocument]",
129+
};
127130
}
128131

129132
pub fn getImplementation(_: *const Document) DOMImplementation {

0 commit comments

Comments
 (0)