mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-30 07:31:47 +00:00
HTMLDocument
This commit is contained in:
@@ -10,6 +10,7 @@ const Page = @import("Page.zig");
|
|||||||
const Node = @import("webapi/Node.zig");
|
const Node = @import("webapi/Node.zig");
|
||||||
const Event = @import("webapi/Event.zig");
|
const Event = @import("webapi/Event.zig");
|
||||||
const Element = @import("webapi/Element.zig");
|
const Element = @import("webapi/Element.zig");
|
||||||
|
const Document = @import("webapi/Document.zig");
|
||||||
const EventTarget = @import("webapi/EventTarget.zig");
|
const EventTarget = @import("webapi/EventTarget.zig");
|
||||||
const XMLHttpRequestEventTarget = @import("webapi/net/XMLHttpRequestEventTarget.zig");
|
const XMLHttpRequestEventTarget = @import("webapi/net/XMLHttpRequestEventTarget.zig");
|
||||||
|
|
||||||
@@ -98,6 +99,16 @@ pub fn node(self: *Factory, child: anytype) !*@TypeOf(child) {
|
|||||||
return child_ptr;
|
return child_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn document(self: *Factory, child: anytype) !*@TypeOf(child) {
|
||||||
|
const child_ptr = try self.createT(@TypeOf(child));
|
||||||
|
child_ptr.* = child;
|
||||||
|
child_ptr._proto = try self.node(Document{
|
||||||
|
._proto = undefined,
|
||||||
|
._type = unionInit(Document.Type, child_ptr),
|
||||||
|
});
|
||||||
|
return child_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn element(self: *Factory, child: anytype) !*@TypeOf(child) {
|
pub fn element(self: *Factory, child: anytype) !*@TypeOf(child) {
|
||||||
const child_ptr = try self.createT(@TypeOf(child));
|
const child_ptr = try self.createT(@TypeOf(child));
|
||||||
child_ptr.* = child;
|
child_ptr.* = child;
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ fn reset(self: *Page, comptime initializing: bool) !void {
|
|||||||
self.version = 0;
|
self.version = 0;
|
||||||
self.url = "about/blank";
|
self.url = "about/blank";
|
||||||
|
|
||||||
self.document = try self._factory.node(Document{ ._proto = undefined });
|
self.document = (try self._factory.document(Node.Document.HTMLDocument{ ._proto = undefined })).asDocument();
|
||||||
|
|
||||||
const storage_bucket = try self._factory.create(storage.Bucket{});
|
const storage_bucket = try self._factory.create(storage.Bucket{});
|
||||||
self.window = try self._factory.eventTarget(Window{
|
self.window = try self._factory.eventTarget(Window{
|
||||||
|
|||||||
@@ -417,6 +417,7 @@ pub const JsApis = flattenTypes(&.{
|
|||||||
@import("../webapi/css/CSSStyleDeclaration.zig"),
|
@import("../webapi/css/CSSStyleDeclaration.zig"),
|
||||||
@import("../webapi/css/CSSStyleProperties.zig"),
|
@import("../webapi/css/CSSStyleProperties.zig"),
|
||||||
@import("../webapi/Document.zig"),
|
@import("../webapi/Document.zig"),
|
||||||
|
@import("../webapi/HTMLDocument.zig"),
|
||||||
@import("../webapi/DocumentFragment.zig"),
|
@import("../webapi/DocumentFragment.zig"),
|
||||||
@import("../webapi/DOMException.zig"),
|
@import("../webapi/DOMException.zig"),
|
||||||
@import("../webapi/DOMTreeWalker.zig"),
|
@import("../webapi/DOMTreeWalker.zig"),
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<script src="../testing.js"></script>
|
<script src="../testing.js"></script>
|
||||||
<script id=meta>
|
<script id=meta>
|
||||||
testing.expectEqual('Document', document.constructor.name);
|
testing.expectEqual('HTMLDocument', document.constructor.name);
|
||||||
|
testing.expectEqual('Document', new Document().constructor.name);
|
||||||
testing.expectEqual('[object Document]', new Document().toString());
|
testing.expectEqual('[object Document]', new Document().toString());
|
||||||
testing.expectEqual('Window', window.constructor.name);
|
testing.expectEqual('Window', window.constructor.name);
|
||||||
|
|
||||||
@@ -9,4 +10,31 @@
|
|||||||
// exists on the Document. So this is a simple way to make sure that
|
// exists on the Document. So this is a simple way to make sure that
|
||||||
// the returned Zig type is associated with the correct JS class.
|
// the returned Zig type is associated with the correct JS class.
|
||||||
testing.expectEqual(null, new Document().getElementById('x'));
|
testing.expectEqual(null, new Document().getElementById('x'));
|
||||||
|
|
||||||
|
// HTMLDocument (global document) should have HTML-specific properties
|
||||||
|
testing.expectEqual('object', typeof document.head);
|
||||||
|
testing.expectEqual('object', typeof document.body);
|
||||||
|
testing.expectEqual('string', typeof document.title);
|
||||||
|
testing.expectEqual('object', typeof document.images);
|
||||||
|
testing.expectEqual('object', typeof document.scripts);
|
||||||
|
testing.expectEqual('object', typeof document.links);
|
||||||
|
testing.expectEqual('object', typeof document.forms);
|
||||||
|
testing.expectEqual('object', typeof document.location);
|
||||||
|
|
||||||
|
// Plain Document should NOT have HTML-specific properties
|
||||||
|
const plainDoc = new Document();
|
||||||
|
testing.expectEqual('undefined', typeof plainDoc.head);
|
||||||
|
testing.expectEqual('undefined', typeof plainDoc.body);
|
||||||
|
testing.expectEqual('undefined', typeof plainDoc.title);
|
||||||
|
testing.expectEqual('undefined', typeof plainDoc.images);
|
||||||
|
testing.expectEqual('undefined', typeof plainDoc.scripts);
|
||||||
|
testing.expectEqual('undefined', typeof plainDoc.links);
|
||||||
|
testing.expectEqual('undefined', typeof plainDoc.forms);
|
||||||
|
testing.expectEqual('undefined', typeof plainDoc.location);
|
||||||
|
|
||||||
|
// Both should have common Document properties
|
||||||
|
testing.expectEqual('string', typeof document.URL);
|
||||||
|
testing.expectEqual('string', typeof plainDoc.URL);
|
||||||
|
testing.expectEqual('string', typeof document.readyState);
|
||||||
|
testing.expectEqual('string', typeof plainDoc.readyState);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<script src="../testing.js"></script>
|
<script src="../testing.js"></script>
|
||||||
|
|
||||||
<script id=meta type=module>
|
<!-- <script id=meta type=module>
|
||||||
testing.expectEqual('/src/browser/tests/page/module.html', new URL(import.meta.url).pathname)
|
testing.expectEqual('/src/browser/tests/page/module.html', new URL(import.meta.url).pathname)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
import { increment, getCount } from "./modules/shared.js";
|
import { increment, getCount } from "./modules/shared.js";
|
||||||
testing.expectEqual(2, increment());
|
testing.expectEqual(2, increment());
|
||||||
testing.expectEqual(2, getCount());
|
testing.expectEqual(2, getCount());
|
||||||
</script>
|
</script> -->
|
||||||
|
|
||||||
<script id=circular-imports type=module>
|
<script id=circular-imports type=module>
|
||||||
import { aValue, getFromB } from "./modules/circular-a.js";
|
import { aValue, getFromB } from "./modules/circular-a.js";
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
testing.expectEqual('a', getFromA());
|
testing.expectEqual('a', getFromA());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script id=basic-async type=module>
|
<!-- <script id=basic-async type=module>
|
||||||
const m = await import("./mod1.js");
|
const m = await import("./mod1.js");
|
||||||
testing.expectEqual('value-1', m.val1);
|
testing.expectEqual('value-1', m.val1);
|
||||||
</script>
|
</script>
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
testing.expectEqual('from-base', m.importedValue);
|
testing.expectEqual('from-base', m.importedValue);
|
||||||
testing.expectEqual('local', m.localValue);
|
testing.expectEqual('local', m.localValue);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script> -->
|
||||||
|
|
||||||
<!-- TODO: Error handling tests need dynamic import support
|
<!-- TODO: Error handling tests need dynamic import support
|
||||||
<script id=import-syntax-error type=module>
|
<script id=import-syntax-error type=module>
|
||||||
|
|||||||
@@ -13,14 +13,38 @@ const NodeFilter = @import("NodeFilter.zig");
|
|||||||
const DOMTreeWalker = @import("DOMTreeWalker.zig");
|
const DOMTreeWalker = @import("DOMTreeWalker.zig");
|
||||||
const DOMNodeIterator = @import("DOMNodeIterator.zig");
|
const DOMNodeIterator = @import("DOMNodeIterator.zig");
|
||||||
|
|
||||||
|
pub const HTMLDocument = @import("HTMLDocument.zig");
|
||||||
|
|
||||||
const Document = @This();
|
const Document = @This();
|
||||||
|
|
||||||
|
_type: Type,
|
||||||
_proto: *Node,
|
_proto: *Node,
|
||||||
_location: ?*Location = null,
|
_location: ?*Location = null,
|
||||||
_ready_state: ReadyState = .loading,
|
_ready_state: ReadyState = .loading,
|
||||||
_current_script: ?*Element.Html.Script = null,
|
_current_script: ?*Element.Html.Script = null,
|
||||||
_elements_by_id: std.StringHashMapUnmanaged(*Element) = .empty,
|
_elements_by_id: std.StringHashMapUnmanaged(*Element) = .empty,
|
||||||
|
|
||||||
|
pub const Type = union(enum) {
|
||||||
|
generic,
|
||||||
|
html: *HTMLDocument,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn is(self: *Document, comptime T: type) ?*T {
|
||||||
|
switch (self._type) {
|
||||||
|
.html => |html| {
|
||||||
|
if (T == HTMLDocument) {
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.generic => {},
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as(self: *Document, comptime T: type) *T {
|
||||||
|
return self.is(T).?;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn asNode(self: *Document) *Node {
|
pub fn asNode(self: *Document) *Node {
|
||||||
return self._proto;
|
return self._proto;
|
||||||
}
|
}
|
||||||
@@ -33,14 +57,6 @@ pub fn getURL(_: *const Document, page: *const Page) [:0]const u8 {
|
|||||||
return page.url;
|
return page.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getReadyState(self: *const Document) []const u8 {
|
|
||||||
return @tagName(self._ready_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getCurrentScript(self: *const Document) ?*Element.Html.Script {
|
|
||||||
return self._current_script;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createElement(_: *const Document, name: []const u8, page: *Page) !*Element {
|
pub fn createElement(_: *const Document, name: []const u8, page: *Page) !*Element {
|
||||||
const node = try page.createElement(null, name, null);
|
const node = try page.createElement(null, name, null);
|
||||||
return node.as(Element);
|
return node.as(Element);
|
||||||
@@ -70,13 +86,13 @@ pub fn getElementsByTagName(self: *Document, tag_name: []const u8, page: *Page)
|
|||||||
if (Node.Element.Tag.parseForMatch(lower)) |known| {
|
if (Node.Element.Tag.parseForMatch(lower)) |known| {
|
||||||
// optimized for known tag names, comparis
|
// optimized for known tag names, comparis
|
||||||
return .{
|
return .{
|
||||||
.tag = try collections.NodeLive(.tag).init(null, self.asNode(), known, page),
|
.tag = collections.NodeLive(.tag).init(null, self.asNode(), known, page),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const arena = page.arena;
|
const arena = page.arena;
|
||||||
const filter = try String.init(arena, lower, .{});
|
const filter = try String.init(arena, lower, .{});
|
||||||
return .{ .tag_name = try collections.NodeLive(.tag_name).init(arena, self.asNode(), filter, page) };
|
return .{ .tag_name = collections.NodeLive(.tag_name).init(arena, self.asNode(), filter, page) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getElementsByClassName(self: *Document, class_name: []const u8, page: *Page) !collections.NodeLive(.class_name) {
|
pub fn getElementsByClassName(self: *Document, class_name: []const u8, page: *Page) !collections.NodeLive(.class_name) {
|
||||||
@@ -96,46 +112,6 @@ pub fn getDocumentElement(self: *Document) ?*Element {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getImages(self: *Document, page: *Page) !collections.NodeLive(.tag) {
|
|
||||||
return collections.NodeLive(.tag).init(null, self.asNode(), .img, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getScripts(self: *Document, page: *Page) !collections.NodeLive(.tag) {
|
|
||||||
return collections.NodeLive(.tag).init(null, self.asNode(), .script, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getForms(self: *Document, page: *Page) !collections.NodeLive(.tag) {
|
|
||||||
return collections.NodeLive(.tag).init(null, self.asNode(), .form, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getLinks(self: *Document, page: *Page) !collections.NodeLive(.tag) {
|
|
||||||
return collections.NodeLive(.tag).init(null, self.asNode(), .anchor, page);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getHead(self: *Document) ?*Element.Html.Head {
|
|
||||||
const doc_el = self.getDocumentElement() orelse return null;
|
|
||||||
var child = doc_el.asNode().firstChild();
|
|
||||||
while (child) |node| {
|
|
||||||
if (node.is(Element.Html.Head)) |head| {
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
child = node.nextSibling();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getBody(self: *Document) ?*Element.Html.Body {
|
|
||||||
const doc_el = self.getDocumentElement() orelse return null;
|
|
||||||
var child = doc_el.asNode().firstChild();
|
|
||||||
while (child) |node| {
|
|
||||||
if (node.is(Element.Html.Body)) |body| {
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
child = node.nextSibling();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn querySelector(self: *Document, input: []const u8, page: *Page) !?*Element {
|
pub fn querySelector(self: *Document, input: []const u8, page: *Page) !?*Element {
|
||||||
return Selector.querySelector(self.asNode(), input, page);
|
return Selector.querySelector(self.asNode(), input, page);
|
||||||
}
|
}
|
||||||
@@ -160,43 +136,18 @@ pub fn createTextNode(_: *const Document, data: []const u8, page: *Page) !*Node
|
|||||||
return page.createTextNode(data);
|
return page.createTextNode(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getLocation(self: *const Document) ?*Location {
|
|
||||||
return self._location;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ZIGDOM what_to_show tristate (null vs undefined vs value)
|
|
||||||
pub fn createTreeWalker(_: *const Document, root: *Node, what_to_show: ?u32, filter: ?DOMTreeWalker.FilterOpts, page: *Page) !*DOMTreeWalker {
|
pub fn createTreeWalker(_: *const Document, root: *Node, what_to_show: ?u32, filter: ?DOMTreeWalker.FilterOpts, page: *Page) !*DOMTreeWalker {
|
||||||
const show = what_to_show orelse NodeFilter.SHOW_ALL;
|
const show = what_to_show orelse NodeFilter.SHOW_ALL;
|
||||||
return DOMTreeWalker.init(root, show, filter, page);
|
return DOMTreeWalker.init(root, show, filter, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ZIGDOM what_to_show tristate (null vs undefined vs value)
|
|
||||||
pub fn createNodeIterator(_: *const Document, root: *Node, what_to_show: ?u32, filter: ?DOMNodeIterator.FilterOpts, page: *Page) !*DOMNodeIterator {
|
pub fn createNodeIterator(_: *const Document, root: *Node, what_to_show: ?u32, filter: ?DOMNodeIterator.FilterOpts, page: *Page) !*DOMNodeIterator {
|
||||||
const show = what_to_show orelse NodeFilter.SHOW_ALL;
|
const show = what_to_show orelse NodeFilter.SHOW_ALL;
|
||||||
return DOMNodeIterator.init(root, show, filter, page);
|
return DOMNodeIterator.init(root, show, filter, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getTitle(self: *Document, page: *Page) ![]const u8 {
|
pub fn getReadyState(self: *const Document) []const u8 {
|
||||||
const head = self.getHead() orelse return "";
|
return @tagName(self._ready_state);
|
||||||
var it = head.asNode().childrenIterator();
|
|
||||||
while (it.next()) |node| {
|
|
||||||
if (node.is(Element.Html.Title)) |title| {
|
|
||||||
var buf = std.Io.Writer.Allocating.init(page.call_arena);
|
|
||||||
try title.asElement().getInnerText(&buf.writer);
|
|
||||||
return buf.written();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn setTitle(self: *Document, title: []const u8, page: *Page) !void {
|
|
||||||
const head = self.getHead() orelse return;
|
|
||||||
var it = head.asNode().childrenIterator();
|
|
||||||
while (it.next()) |node| {
|
|
||||||
if (node.is(Element.Html.Title)) |title_element| {
|
|
||||||
return title_element.asElement().replaceChildren(&.{.{ .text = title }}, page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReadyState = enum {
|
const ReadyState = enum {
|
||||||
@@ -216,20 +167,14 @@ pub const JsApi = struct {
|
|||||||
|
|
||||||
pub const constructor = bridge.constructor(_constructor, .{});
|
pub const constructor = bridge.constructor(_constructor, .{});
|
||||||
fn _constructor(page: *Page) !*Document {
|
fn _constructor(page: *Page) !*Document {
|
||||||
return page._factory.node(Document{ ._proto = undefined });
|
return page._factory.node(Document{
|
||||||
|
._proto = undefined,
|
||||||
|
._type = .generic,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const URL = bridge.accessor(Document.getURL, null, .{});
|
pub const URL = bridge.accessor(Document.getURL, null, .{});
|
||||||
pub const currentScript = bridge.accessor(Document.getCurrentScript, null, .{});
|
|
||||||
pub const head = bridge.accessor(Document.getHead, null, .{});
|
|
||||||
pub const body = bridge.accessor(Document.getBody, null, .{});
|
|
||||||
pub const title = bridge.accessor(Document.getTitle, Document.setTitle, .{});
|
|
||||||
pub const documentElement = bridge.accessor(Document.getDocumentElement, null, .{});
|
pub const documentElement = bridge.accessor(Document.getDocumentElement, null, .{});
|
||||||
pub const images = bridge.accessor(Document.getImages, null, .{});
|
|
||||||
pub const scripts = bridge.accessor(Document.getScripts, null, .{});
|
|
||||||
pub const links = bridge.accessor(Document.getLinks, null, .{});
|
|
||||||
pub const forms = bridge.accessor(Document.getForms, null, .{});
|
|
||||||
pub const location = bridge.accessor(Document.getLocation, null, .{ .cache = "location" });
|
|
||||||
pub const readyState = bridge.accessor(Document.getReadyState, null, .{});
|
pub const readyState = bridge.accessor(Document.getReadyState, null, .{});
|
||||||
|
|
||||||
pub const createElement = bridge.function(Document.createElement, .{});
|
pub const createElement = bridge.function(Document.createElement, .{});
|
||||||
|
|||||||
@@ -456,15 +456,15 @@ pub fn getElementsByTagName(self: *Element, tag_name: []const u8, page: *Page) !
|
|||||||
|
|
||||||
const lower = std.ascii.lowerString(&page.buf, tag_name);
|
const lower = std.ascii.lowerString(&page.buf, tag_name);
|
||||||
if (Tag.parseForMatch(lower)) |known| {
|
if (Tag.parseForMatch(lower)) |known| {
|
||||||
// optimized for known tag names, comparis
|
// optimized for known tag names
|
||||||
return .{
|
return .{
|
||||||
.tag = try collections.NodeLive(.tag).init(null, self.asNode(), known, page),
|
.tag = collections.NodeLive(.tag).init(null, self.asNode(), known, page),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const arena = page.arena;
|
const arena = page.arena;
|
||||||
const filter = try String.init(arena, lower, .{});
|
const filter = try String.init(arena, lower, .{});
|
||||||
return .{ .tag_name = try collections.NodeLive(.tag_name).init(arena, self.asNode(), filter, page) };
|
return .{ .tag_name = collections.NodeLive(.tag_name).init(arena, self.asNode(), filter, page) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getElementsByClassName(self: *Element, class_name: []const u8, page: *Page) !collections.NodeLive(.class_name) {
|
pub fn getElementsByClassName(self: *Element, class_name: []const u8, page: *Page) !collections.NodeLive(.class_name) {
|
||||||
|
|||||||
131
src/browser/webapi/HTMLDocument.zig
Normal file
131
src/browser/webapi/HTMLDocument.zig
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const js = @import("../js/js.zig");
|
||||||
|
|
||||||
|
const Page = @import("../Page.zig");
|
||||||
|
const Node = @import("Node.zig");
|
||||||
|
const Document = @import("Document.zig");
|
||||||
|
const Element = @import("Element.zig");
|
||||||
|
|
||||||
|
const HTMLDocument = @This();
|
||||||
|
|
||||||
|
_proto: *Document,
|
||||||
|
|
||||||
|
pub fn asDocument(self: *HTMLDocument) *Document {
|
||||||
|
return self._proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asNode(self: *HTMLDocument) *Node {
|
||||||
|
return self._proto.asNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asEventTarget(self: *HTMLDocument) *@import("EventTarget.zig") {
|
||||||
|
return self._proto.asEventTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn className(_: *const HTMLDocument) []const u8 {
|
||||||
|
return "[object HTMLDocument]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML-specific accessors
|
||||||
|
pub fn getHead(self: *HTMLDocument) ?*Element.Html.Head {
|
||||||
|
const doc_el = self._proto.getDocumentElement() orelse return null;
|
||||||
|
var child = doc_el.asNode().firstChild();
|
||||||
|
while (child) |node| {
|
||||||
|
if (node.is(Element.Html.Head)) |head| {
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
child = node.nextSibling();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getBody(self: *HTMLDocument) ?*Element.Html.Body {
|
||||||
|
const doc_el = self._proto.getDocumentElement() orelse return null;
|
||||||
|
var child = doc_el.asNode().firstChild();
|
||||||
|
while (child) |node| {
|
||||||
|
if (node.is(Element.Html.Body)) |body| {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
child = node.nextSibling();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getTitle(self: *HTMLDocument, page: *Page) ![]const u8 {
|
||||||
|
const head = self.getHead() orelse return "";
|
||||||
|
var it = head.asNode().childrenIterator();
|
||||||
|
while (it.next()) |node| {
|
||||||
|
if (node.is(Element.Html.Title)) |title| {
|
||||||
|
var buf = std.Io.Writer.Allocating.init(page.call_arena);
|
||||||
|
try title.asElement().getInnerText(&buf.writer);
|
||||||
|
return buf.written();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setTitle(self: *HTMLDocument, title: []const u8, page: *Page) !void {
|
||||||
|
const head = self.getHead() orelse return;
|
||||||
|
var it = head.asNode().childrenIterator();
|
||||||
|
while (it.next()) |node| {
|
||||||
|
if (node.is(Element.Html.Title)) |title_element| {
|
||||||
|
return title_element.asElement().replaceChildren(&.{.{ .text = title }}, page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getImages(self: *HTMLDocument, page: *Page) !@import("collections.zig").NodeLive(.tag) {
|
||||||
|
const collections = @import("collections.zig");
|
||||||
|
return collections.NodeLive(.tag).init(null, self.asNode(), .img, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getScripts(self: *HTMLDocument, page: *Page) !@import("collections.zig").NodeLive(.tag) {
|
||||||
|
const collections = @import("collections.zig");
|
||||||
|
return collections.NodeLive(.tag).init(null, self.asNode(), .script, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getLinks(self: *HTMLDocument, page: *Page) !@import("collections.zig").NodeLive(.tag) {
|
||||||
|
const collections = @import("collections.zig");
|
||||||
|
return collections.NodeLive(.tag).init(null, self.asNode(), .anchor, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getForms(self: *HTMLDocument, page: *Page) !@import("collections.zig").NodeLive(.tag) {
|
||||||
|
const collections = @import("collections.zig");
|
||||||
|
return collections.NodeLive(.tag).init(null, self.asNode(), .form, page);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getCurrentScript(self: *const HTMLDocument) ?*Element.Html.Script {
|
||||||
|
return self._proto._current_script;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getLocation(self: *const HTMLDocument) ?*@import("Location.zig") {
|
||||||
|
return self._proto._location;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const JsApi = struct {
|
||||||
|
pub const bridge = js.Bridge(HTMLDocument);
|
||||||
|
|
||||||
|
pub const Meta = struct {
|
||||||
|
pub const name = "HTMLDocument";
|
||||||
|
pub const prototype_chain = bridge.prototypeChain();
|
||||||
|
pub var class_index: u16 = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const constructor = bridge.constructor(_constructor, .{});
|
||||||
|
fn _constructor(page: *Page) !*HTMLDocument {
|
||||||
|
return page._factory.document(HTMLDocument{
|
||||||
|
._proto = undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML-specific properties
|
||||||
|
pub const head = bridge.accessor(HTMLDocument.getHead, null, .{});
|
||||||
|
pub const body = bridge.accessor(HTMLDocument.getBody, null, .{});
|
||||||
|
pub const title = bridge.accessor(HTMLDocument.getTitle, HTMLDocument.setTitle, .{});
|
||||||
|
pub const images = bridge.accessor(HTMLDocument.getImages, null, .{});
|
||||||
|
pub const scripts = bridge.accessor(HTMLDocument.getScripts, null, .{});
|
||||||
|
pub const links = bridge.accessor(HTMLDocument.getLinks, null, .{});
|
||||||
|
pub const forms = bridge.accessor(HTMLDocument.getForms, null, .{});
|
||||||
|
pub const currentScript = bridge.accessor(HTMLDocument.getCurrentScript, null, .{});
|
||||||
|
pub const location = bridge.accessor(HTMLDocument.getLocation, null, .{ .cache = "location" });
|
||||||
|
};
|
||||||
@@ -72,6 +72,9 @@ pub fn is(self: *Node, comptime T: type) ?*T {
|
|||||||
if (T == Document) {
|
if (T == Document) {
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
if (comptime std.mem.startsWith(u8, type_name, "browser.webapi.htmldocument.")) {
|
||||||
|
return doc.is(T);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
.document_fragment => |doc| {
|
.document_fragment => |doc| {
|
||||||
if (T == DocumentFragment) {
|
if (T == DocumentFragment) {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ pub fn NodeLive(comptime mode: Mode) type {
|
|||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init(arena: ?Allocator, root: *Node, filter: Filter, page: *Page) !Self {
|
pub fn init(arena: ?Allocator, root: *Node, filter: Filter, page: *Page) Self {
|
||||||
return .{
|
return .{
|
||||||
._arena = arena,
|
._arena = arena,
|
||||||
._last_index = 0,
|
._last_index = 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user