diff --git a/src/browser/dump.zig b/src/browser/dump.zig
index 22460ee5..620ef470 100644
--- a/src/browser/dump.zig
+++ b/src/browser/dump.zig
@@ -26,6 +26,7 @@ pub fn deep(node: *Node, opts: Opts, writer: *std.Io.Writer) error{WriteFailed}!
}
},
.document => try children(node, opts, writer),
+ .document_type => {},
.document_fragment => try children(node, opts, writer),
.attribute => unreachable,
}
@@ -49,6 +50,9 @@ pub fn toJSON(node: *Node, writer: *std.json.Stringify) !void {
.document => {
try writer.write("document");
},
+ .document_type => {
+ try writer.write("document_type");
+ },
.element => |*el| {
try writer.write("element");
try writer.objectField("tag");
diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig
index 01a67e72..95d2bb29 100644
--- a/src/browser/js/bridge.zig
+++ b/src/browser/js/bridge.zig
@@ -446,7 +446,9 @@ pub const JsApis = flattenTypes(&.{
@import("../webapi/HTMLDocument.zig"),
@import("../webapi/KeyValueList.zig"),
@import("../webapi/DocumentFragment.zig"),
+ @import("../webapi/DocumentType.zig"),
@import("../webapi/DOMException.zig"),
+ @import("../webapi/DOMImplementation.zig"),
@import("../webapi/DOMTreeWalker.zig"),
@import("../webapi/DOMNodeIterator.zig"),
@import("../webapi/NodeFilter.zig"),
diff --git a/src/browser/tests/domimplementation.html b/src/browser/tests/domimplementation.html
new file mode 100644
index 00000000..7ff1b0e0
--- /dev/null
+++ b/src/browser/tests/domimplementation.html
@@ -0,0 +1,61 @@
+
+
+ DOMImplementation Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/DOMImplementation.zig b/src/browser/webapi/DOMImplementation.zig
new file mode 100644
index 00000000..93028f74
--- /dev/null
+++ b/src/browser/webapi/DOMImplementation.zig
@@ -0,0 +1,57 @@
+const std = @import("std");
+
+const js = @import("../js/js.zig");
+const Page = @import("../Page.zig");
+const Node = @import("Node.zig");
+const DocumentType = @import("DocumentType.zig");
+
+const DOMImplementation = @This();
+
+pub fn createDocumentType(_: *const DOMImplementation, qualified_name: []const u8, public_id: ?[]const u8, system_id: ?[]const u8, page: *Page) !*DocumentType {
+ const name = try page.dupeString(qualified_name);
+ const pub_id = try page.dupeString(public_id orelse "");
+ const sys_id = try page.dupeString(system_id orelse "");
+
+ const doctype = try page._factory.node(DocumentType{
+ ._proto = undefined,
+ ._name = name,
+ ._public_id = pub_id,
+ ._system_id = sys_id,
+ });
+
+ return doctype;
+}
+
+pub fn hasFeature(_: *const DOMImplementation, _: []const u8, _: ?[]const u8) bool {
+ // Modern DOM spec says this should always return true
+ // This method is deprecated and kept for compatibility only
+ return true;
+}
+
+pub fn className(_: *const DOMImplementation) []const u8 {
+ return "[object DOMImplementation]";
+}
+
+pub const JsApi = struct {
+ pub const bridge = js.Bridge(DOMImplementation);
+
+ pub const Meta = struct {
+ pub const name = "DOMImplementation";
+ pub const prototype_chain = bridge.prototypeChain();
+ pub var class_index: u16 = 0;
+ pub const empty_with_no_proto = true;
+ };
+
+ pub const createDocumentType = bridge.function(DOMImplementation.createDocumentType, .{ .dom_exception = true });
+ pub const hasFeature = bridge.function(DOMImplementation.hasFeature, .{});
+
+ pub const toString = bridge.function(_toString, .{});
+ fn _toString(_: *const DOMImplementation) []const u8 {
+ return "[object DOMImplementation]";
+ }
+};
+
+const testing = @import("../../testing.zig");
+test "WebApi: DOMImplementation" {
+ try testing.htmlRunner("domimplementation.html", .{});
+}
diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig
index 2bd55443..ba364df4 100644
--- a/src/browser/webapi/Document.zig
+++ b/src/browser/webapi/Document.zig
@@ -12,6 +12,7 @@ const Selector = @import("selector/Selector.zig");
const NodeFilter = @import("NodeFilter.zig");
const DOMTreeWalker = @import("DOMTreeWalker.zig");
const DOMNodeIterator = @import("DOMNodeIterator.zig");
+const DOMImplementation = @import("DOMImplementation.zig");
pub const HTMLDocument = @import("HTMLDocument.zig");
@@ -124,6 +125,10 @@ pub fn className(_: *const Document) []const u8 {
return "[object Document]";
}
+pub fn getImplementation(_: *const Document) DOMImplementation {
+ return .{};
+}
+
pub fn createDocumentFragment(_: *const Document, page: *Page) !*@import("DocumentFragment.zig") {
return @import("DocumentFragment.zig").init(page);
}
@@ -176,6 +181,7 @@ pub const JsApi = struct {
pub const URL = bridge.accessor(Document.getURL, null, .{});
pub const documentElement = bridge.accessor(Document.getDocumentElement, null, .{});
pub const readyState = bridge.accessor(Document.getReadyState, null, .{});
+ pub const implementation = bridge.accessor(Document.getImplementation, null, .{});
pub const createElement = bridge.function(Document.createElement, .{});
pub const createElementNS = bridge.function(Document.createElementNS, .{});
diff --git a/src/browser/webapi/DocumentType.zig b/src/browser/webapi/DocumentType.zig
new file mode 100644
index 00000000..9678e0bf
--- /dev/null
+++ b/src/browser/webapi/DocumentType.zig
@@ -0,0 +1,60 @@
+const std = @import("std");
+
+const js = @import("../js/js.zig");
+const Page = @import("../Page.zig");
+const Node = @import("Node.zig");
+
+const DocumentType = @This();
+
+_proto: *Node,
+_name: []const u8,
+_public_id: []const u8,
+_system_id: []const u8,
+
+pub fn asNode(self: *DocumentType) *Node {
+ return self._proto;
+}
+
+pub fn asEventTarget(self: *DocumentType) *@import("EventTarget.zig") {
+ return self._proto.asEventTarget();
+}
+
+pub fn getName(self: *const DocumentType) []const u8 {
+ return self._name;
+}
+
+pub fn getPublicId(self: *const DocumentType) []const u8 {
+ return self._public_id;
+}
+
+pub fn getSystemId(self: *const DocumentType) []const u8 {
+ return self._system_id;
+}
+
+pub fn className(_: *const DocumentType) []const u8 {
+ return "[object DocumentType]";
+}
+
+pub const JsApi = struct {
+ pub const bridge = js.Bridge(DocumentType);
+
+ pub const Meta = struct {
+ pub const name = "DocumentType";
+ pub const prototype_chain = bridge.prototypeChain();
+ pub var class_index: u16 = 0;
+ };
+
+ pub const name = bridge.accessor(DocumentType.getName, null, .{});
+ pub const publicId = bridge.accessor(DocumentType.getPublicId, null, .{});
+ pub const systemId = bridge.accessor(DocumentType.getSystemId, null, .{});
+
+ pub const toString = bridge.function(_toString, .{});
+ fn _toString(self: *const DocumentType) []const u8 {
+ return self.className();
+ }
+};
+
+const testing = @import("../../testing.zig");
+test "WebApi: DOMImplementation" {
+ try testing.htmlRunner("domimplementation.html", .{});
+}
diff --git a/src/browser/webapi/Node.zig b/src/browser/webapi/Node.zig
index d4ca9154..4be5eeac 100644
--- a/src/browser/webapi/Node.zig
+++ b/src/browser/webapi/Node.zig
@@ -11,8 +11,10 @@ const collections = @import("collections.zig");
pub const CData = @import("CData.zig");
pub const Element = @import("Element.zig");
pub const Document = @import("Document.zig");
+pub const HTMLDocument = @import("HTMLDocument.zig");
pub const Children = @import("children.zig").Children;
pub const DocumentFragment = @import("DocumentFragment.zig");
+pub const DocumentType = @import("DocumentType.zig");
const Allocator = std.mem.Allocator;
const LinkedList = std.DoublyLinkedList;
@@ -29,6 +31,7 @@ pub const Type = union(enum) {
cdata: *CData,
element: *Element,
document: *Document,
+ document_type: *DocumentType,
attribute: *Element.Attribute,
document_fragment: *DocumentFragment,
};
@@ -76,6 +79,11 @@ pub fn is(self: *Node, comptime T: type) ?*T {
return doc.is(T);
}
},
+ .document_type => |dt| {
+ if (T == DocumentType) {
+ return dt;
+ }
+ },
.document_fragment => |doc| {
if (T == DocumentFragment) {
return doc;
@@ -145,6 +153,7 @@ pub fn getTextContent(self: *Node, writer: *std.Io.Writer) error{WriteFailed}!vo
.element => |el| return el.getInnerText(writer),
.cdata => |c| try writer.writeAll(c.getData()),
.document => {},
+ .document_type => {},
.document_fragment => {},
.attribute => |attr| try writer.writeAll(attr._value),
}
@@ -163,6 +172,7 @@ pub fn setTextContent(self: *Node, data: []const u8, page: *Page) !void {
.element => |el| return el.replaceChildren(&.{.{ .text = data }}, page),
.cdata => |c| c._data = try page.arena.dupe(u8, data),
.document => {},
+ .document_type => {},
.document_fragment => {},
.attribute => |attr| return attr.setValue(data, page),
}
@@ -176,6 +186,7 @@ pub fn getNodeName(self: *const Node, page: *Page) ![]const u8 {
.comment => "#comment",
},
.document => "#document",
+ .document_type => |dt| dt.getName(),
.document_fragment => "#document-fragment",
.attribute => |attr| attr._name,
};
@@ -190,6 +201,7 @@ pub fn nodeType(self: *const Node) u8 {
.comment => 8,
},
.document => 9,
+ .document_type => 10,
.document_fragment => 11,
};
}
@@ -333,6 +345,7 @@ pub fn getNodeValue(self: *const Node) ?[]const u8 {
.attribute => |attr| attr._value,
.element => null,
.document => null,
+ .document_type => null,
.document_fragment => null,
};
}
@@ -343,6 +356,7 @@ pub fn setNodeValue(self: *const Node, value: ?[]const u8, page: *Page) !void {
.attribute => |attr| try attr.setValue(value, page),
.element => {},
.document => {},
+ .document_type => {},
.document_fragment => {},
}
}
@@ -350,11 +364,11 @@ pub fn setNodeValue(self: *const Node, value: ?[]const u8, page: *Page) !void {
pub fn format(self: *Node, writer: *std.Io.Writer) !void {
// // If you need extra debugging:
// return @import("../dump.zig").deep(self, .{}, writer);
-
return switch (self._type) {
.cdata => |cd| cd.format(writer),
.element => |el| writer.print("{f}", .{el}),
.document => writer.writeAll(""),
+ .document_type => writer.writeAll(""),
.document_fragment => writer.writeAll(""),
.attribute => |attr| writer.print("{f}", .{attr}),
};
@@ -380,8 +394,7 @@ pub fn className(self: *const Node) []const u8 {
pub fn normalize(self: *Node, page: *Page) !void {
var buffer: std.ArrayListUnmanaged(u8) = .empty;
- const arena = page.call_arena;
- return self._normalize(arena, &buffer, page);
+ return self._normalize(page.call_arena, &buffer, page);
}
pub fn cloneNode(self: *Node, deep_: ?bool, page: *Page) error{ OutOfMemory, StringTooLarge, NotSupported, NotImplemented }!*Node {
@@ -396,6 +409,7 @@ pub fn cloneNode(self: *Node, deep_: ?bool, page: *Page) error{ OutOfMemory, Str
},
.element => |el| return el.cloneElement(deep, page),
.document => return error.NotSupported,
+ .document_type => return error.NotSupported,
.document_fragment => return error.NotImplemented,
.attribute => return error.NotSupported,
}
@@ -612,6 +626,7 @@ pub const JsApi = struct {
.cdata => |cdata| return cdata.getData(),
.attribute => |attr| return attr._value,
.document => return null,
+ .document_type => return null,
.document_fragment => return null,
}
}
diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig
index 92ad27e3..a288e9e5 100644
--- a/src/browser/webapi/Window.zig
+++ b/src/browser/webapi/Window.zig
@@ -249,6 +249,7 @@ pub const JsApi = struct {
pub const self = bridge.accessor(Window.getWindow, null, .{ .cache = "self" });
pub const window = bridge.accessor(Window.getWindow, null, .{ .cache = "window" });
+ pub const parent = bridge.accessor(Window.getWindow, null, .{ .cache = "parent" });
pub const console = bridge.accessor(Window.getConsole, null, .{ .cache = "console" });
pub const navigator = bridge.accessor(Window.getNavigator, null, .{ .cache = "navigator" });
pub const localStorage = bridge.accessor(Window.getLocalStorage, null, .{ .cache = "localStorage" });