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" });