Element legacy test passing

This commit is contained in:
Karl Seguin
2025-12-11 12:51:56 +08:00
parent b25e46de2e
commit 34f0857b4f
12 changed files with 356 additions and 44 deletions

View File

@@ -181,3 +181,80 @@
document.cookie = 'IgnoreMy=Ghost; HttpOnly'; document.cookie = 'IgnoreMy=Ghost; HttpOnly';
testing.expectEqual('name=Oeschger; favorite_food=tripe', document.cookie); testing.expectEqual('name=Oeschger; favorite_food=tripe', document.cookie);
</script> </script>
<script id=createAttribute>
{
var attr = document.createAttribute('hello');
testing.expectEqual('hello', attr.name);
testing.expectEqual('', attr.value);
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => document.createAttribute(''));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => document.createAttribute('.over'));
}
</script>
<script id=append>
{
const doc = new Document();
const html = doc.createElement('html');
const body = doc.createElement('body');
const div1 = doc.createElement('div');
const div2 = doc.createElement('div');
doc.append(html);
testing.expectEqual(1, doc.childNodes.length);
testing.expectEqual(html, doc.childNodes[0]);
html.append(body);
testing.expectEqual(1, html.childNodes.length);
testing.expectEqual(body, html.childNodes[0]);
body.append(div1, div2);
testing.expectEqual(2, body.childNodes.length);
testing.expectEqual(div1, body.childNodes[0]);
testing.expectEqual(div2, body.childNodes[1]);
body.append('text node');
testing.expectEqual(3, body.childNodes.length);
testing.expectEqual(3, body.childNodes[2].nodeType);
testing.expectEqual('text node', body.childNodes[2].textContent);
}
</script>
<script id=prepend>
{
const doc = new Document();
const html = doc.createElement('html');
const body = doc.createElement('body');
const div1 = doc.createElement('div');
const div2 = doc.createElement('div');
const div3 = doc.createElement('div');
doc.prepend(html);
testing.expectEqual(1, doc.childNodes.length);
testing.expectEqual(html, doc.childNodes[0]);
html.prepend(body);
testing.expectEqual(1, html.childNodes.length);
testing.expectEqual(body, html.childNodes[0]);
body.append(div1);
body.prepend(div2, div3);
testing.expectEqual(3, body.childNodes.length);
testing.expectEqual(div2, body.childNodes[0]);
testing.expectEqual(div3, body.childNodes[1]);
testing.expectEqual(div1, body.childNodes[2]);
body.prepend('text node');
testing.expectEqual(4, body.childNodes.length);
testing.expectEqual(3, body.childNodes[0].nodeType);
testing.expectEqual('text node', body.childNodes[0].textContent);
}
</script>

View File

@@ -165,3 +165,84 @@
testing.expectEqual(false, div.hasAttributes()); testing.expectEqual(false, div.hasAttributes());
} }
</script> </script>
<script id=invalidAttributeNames>
{
const div = document.createElement('div');
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.setAttribute('0abc', 'value'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.setAttribute('123', 'value'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.setAttribute('-invalid', 'value'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.setAttribute('.foo', 'value'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.setAttribute('my attr', 'value'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.setAttribute('my@attr', 'value'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.setAttribute('attr!', 'value'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.setAttribute('~', 'value'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.setAttribute('', 'value'));
div.setAttribute('valid-name', 'value1');
testing.expectEqual('value1', div.getAttribute('valid-name'));
div.setAttribute('valid_name', 'value2');
testing.expectEqual('value2', div.getAttribute('valid_name'));
div.setAttribute('valid.name', 'value3');
testing.expectEqual('value3', div.getAttribute('valid.name'));
div.setAttribute('a123', 'value4');
testing.expectEqual('value4', div.getAttribute('a123'));
div.setAttribute('data-test-123', 'value5');
testing.expectEqual('value5', div.getAttribute('data-test-123'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.toggleAttribute('.invalid'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.toggleAttribute('0invalid'));
testing.withError((err) => {
testing.expectEqual(5, err.code);
testing.expectEqual("InvalidCharacterError", err.name);
}, () => div.toggleAttribute('-invalid'));
}
</script>

View File

@@ -9,7 +9,7 @@
<span id="span1">Span 1</span> <span id="span1">Span 1</span>
<p id="p2">Paragraph 2</p> <p id="p2">Paragraph 2</p>
</div> </div>
<div id="empty"></div> <div id="empty" dir=ltr></div>
<script id="relatedElements"> <script id="relatedElements">
const container = $('#container'); const container = $('#container');
@@ -65,3 +65,91 @@
p.textContent = 'XAnge\xa0Privacy'; p.textContent = 'XAnge\xa0Privacy';
testing.expectEqual('<p>XAnge&nbsp;Privacy</p>', p.outerHTML); testing.expectEqual('<p>XAnge&nbsp;Privacy</p>', p.outerHTML);
</script> </script>
<script id=element>
{
const empty = $('#empty');
testing.expectEqual('empty', empty.id);
testing.expectEqual('ltr', empty.dir);
// good enough that it doesn't throw
empty.scrollIntoViewIfNeeded();
empty.scrollIntoViewIfNeeded(false);
}
</script>
<script id=before>
{
const parent = document.createElement('div');
const existing = document.createElement('span');
parent.appendChild(existing);
const div1 = document.createElement('div');
const div2 = document.createElement('div');
existing.before(div1, div2);
testing.expectEqual(3, parent.childNodes.length);
testing.expectEqual(div1, parent.childNodes[0]);
testing.expectEqual(div2, parent.childNodes[1]);
testing.expectEqual(existing, parent.childNodes[2]);
existing.before('text node');
testing.expectEqual(4, parent.childNodes.length);
testing.expectEqual(3, parent.childNodes[2].nodeType);
testing.expectEqual('text node', parent.childNodes[2].textContent);
testing.expectEqual(existing, parent.childNodes[3]);
const detached = document.createElement('div');
detached.before(document.createElement('p'));
testing.expectEqual(null, detached.parentNode);
}
</script>
<script id=after>
{
const parent = document.createElement('div');
const existing = document.createElement('span');
parent.appendChild(existing);
const div1 = document.createElement('div');
const div2 = document.createElement('div');
existing.after(div1, div2);
testing.expectEqual(3, parent.childNodes.length);
testing.expectEqual(existing, parent.childNodes[0]);
testing.expectEqual(div1, parent.childNodes[1]);
testing.expectEqual(div2, parent.childNodes[2]);
existing.after('text node');
testing.expectEqual(4, parent.childNodes.length);
testing.expectEqual(existing, parent.childNodes[0]);
testing.expectEqual(3, parent.childNodes[1].nodeType);
testing.expectEqual('text node', parent.childNodes[1].textContent);
const detached = document.createElement('div');
detached.after(document.createElement('p'));
testing.expectEqual(null, detached.parentNode);
}
</script>
<script id=beforeAfterOrdering>
{
const parent = document.createElement('div');
const a = document.createElement('a');
const b = document.createElement('b');
const c = document.createElement('c');
const d = document.createElement('d');
parent.appendChild(b);
parent.appendChild(c);
b.before(a);
c.after(d);
testing.expectEqual(4, parent.childNodes.length);
testing.expectEqual(a, parent.childNodes[0]);
testing.expectEqual(b, parent.childNodes[1]);
testing.expectEqual(c, parent.childNodes[2]);
testing.expectEqual(d, parent.childNodes[3]);
}
</script>

View File

@@ -10,6 +10,8 @@
<script id=querySelector"> <script id=querySelector">
const p1 = $('#p1'); const p1 = $('#p1');
testing.expectEqual(null, p1.querySelector('#p1'));
testing.expectError("Syntax Error", () => p1.querySelector('')); testing.expectError("Syntax Error", () => p1.querySelector(''));
testing.withError((err) => { testing.withError((err) => {
testing.expectEqual(12, err.code); testing.expectEqual(12, err.code);

View File

@@ -85,6 +85,9 @@
const all = root.querySelectorAll('*'); const all = root.querySelectorAll('*');
testing.expectEqual(true, all.length >= 6); testing.expectEqual(true, all.length >= 6);
testing.expectEqual('Item 1', all[0].textContent); testing.expectEqual('Item 1', all[0].textContent);
testing.expectEqual('Item 1', all.item(0).textContent);
testing.expectEqual(null, all.item(99));
testing.expectEqual(undefined, all[99]);
const items = root.querySelectorAll('*.item'); const items = root.querySelectorAll('*.item');
testing.expectEqual(4, items.length); testing.expectEqual(4, items.length);

View File

@@ -43,13 +43,11 @@
testing.expectEqual(el2, el2.closest('#closest')); testing.expectEqual(el2, el2.closest('#closest'));
testing.expectEqual(el2, el2.closest('.ok')); testing.expectEqual(el2, el2.closest('.ok'));
testing.expectEqual(null, el2.closest('#9000'));
testing.expectEqual(null, el2.closest('.notok')); testing.expectEqual(null, el2.closest('.notok'));
const sp = document.createElement('span'); const sp = document.createElement('span');
el2.appendChild(sp); el2.appendChild(sp);
testing.expectEqual(el2, sp.closest('#closest')); testing.expectEqual(el2, sp.closest('#closest'));
testing.expectEqual(null, sp.closest('#9000'));
</script> </script>
<script id=attributes> <script id=attributes>
@@ -73,7 +71,7 @@
testing.expectEqual('bar', content.getAttribute('foo')); testing.expectEqual('bar', content.getAttribute('foo'));
testing.expectEqual(['id', 'dir', 'foo'], content.getAttributeNames()); testing.expectEqual(['id', 'dir', 'foo'], content.getAttributeNames());
testing.expectError('Error: InvalidCharacterError', () => { testing.expectError('Error: Invalid Character', () => {
content.setAttribute('.foo', 'invalid') content.setAttribute('.foo', 'invalid')
}); });
@@ -123,7 +121,6 @@
testing.expectEqual('link', content.querySelector('#link').id); testing.expectEqual('link', content.querySelector('#link').id);
testing.expectEqual('para', content.querySelector('#para').id); testing.expectEqual('para', content.querySelector('#para').id);
testing.expectEqual('link', content.querySelector('*').id); testing.expectEqual('link', content.querySelector('*').id);
testing.expectEqual(null, content.querySelector(''));
testing.expectEqual('link', content.querySelector('*').id); testing.expectEqual('link', content.querySelector('*').id);
testing.expectEqual(null, content.querySelector('#content')); testing.expectEqual(null, content.querySelector('#content'));
testing.expectEqual('para', content.querySelector('#para').id); testing.expectEqual('para', content.querySelector('#para').id);
@@ -166,46 +163,12 @@
testing.expectEqual('<p id="para"> And</p>', $('#para').outerHTML); testing.expectEqual('<p id="para"> And</p>', $('#para').outerHTML);
</script> </script>
<script id=dimensions>
const para = document.getElementById('para');
testing.expectEqual(5, para.clientWidth);
testing.expectEqual(5, para.clientHeight);
let r1 = document.getElementById('para').getBoundingClientRect();
testing.expectEqual(0, r1.x);
testing.expectEqual(0, r1.y);
testing.expectEqual(5, r1.width);
testing.expectEqual(5, r1.height);
let r2 = document.getElementById('content').getBoundingClientRect();
testing.expectEqual(5, r2.x);
testing.expectEqual(0, r2.y);
testing.expectEqual(5, r2.width);
testing.expectEqual(5, r2.height);
let r3 = document.getElementById('para').getBoundingClientRect();
testing.expectEqual(0, r3.x);
testing.expectEqual(0, r3.y);
testing.expectEqual(5, r3.width);
testing.expectEqual(5, r3.height);
testing.expectEqual(10, para.clientWidth);
testing.expectEqual(5, para.clientHeight);
let r4 = document.createElement('div').getBoundingClientRect();
testing.expectEqual(0, r4.x);
testing.expectEqual(0, r4.y);
testing.expectEqual(0, r4.width);
testing.expectEqual(0, r4.height);
</script>
<script id=matches> <script id=matches>
const el = document.createElement('div'); const el = document.createElement('div');
el.id = 'matches'; el.id = 'matches';
el.className = 'ok'; el.className = 'ok';
testing.expectEqual(true, el.matches('#matches')); testing.expectEqual(true, el.matches('#matches'));
testing.expectEqual(true, el.matches('.ok')); testing.expectEqual(true, el.matches('.ok'));
testing.expectEqual(false, el.matches('#9000'));
testing.expectEqual(false, el.matches('.notok')); testing.expectEqual(false, el.matches('.notok'));
</script> </script>
@@ -338,4 +301,4 @@
const p = document.createElement('p'); const p = document.createElement('p');
p.textContent = 'XAnge\xa0Privacy'; p.textContent = 'XAnge\xa0Privacy';
testing.expectEqual('<p>XAnge&nbsp;Privacy</p>', p.outerHTML); testing.expectEqual('<p>XAnge&nbsp;Privacy</p>', p.outerHTML);
</script> </script> -->

View File

@@ -57,7 +57,7 @@ pub fn getName(self: *const DOMException) []const u8 {
pub fn getMessage(self: *const DOMException) []const u8 { pub fn getMessage(self: *const DOMException) []const u8 {
return switch (self._code) { return switch (self._code) {
.none => "", .none => "",
.invalid_character_error => "Invalid Character", .invalid_character_error => "Error: Invalid Character",
.index_size_error => "IndexSizeError: Index or size is negative or greater than the allowed amount", .index_size_error => "IndexSizeError: Index or size is negative or greater than the allowed amount",
.syntax_error => "Syntax Error", .syntax_error => "Syntax Error",
.not_supported => "Not Supported", .not_supported => "Not Supported",

View File

@@ -125,6 +125,16 @@ pub fn createElementNS(_: *const Document, namespace: ?[]const u8, name: []const
return node.as(Element); return node.as(Element);
} }
pub fn createAttribute(_: *const Document, name: []const u8, page: *Page) !?*Element.Attribute {
try Element.Attribute.validateAttributeName(name);
return page._factory.node(Element.Attribute{
._proto = undefined,
._name = try page.dupeString(name),
._value = "",
._element = null,
});
}
pub fn getElementById(self: *const Document, id_: ?[]const u8) ?*Element { pub fn getElementById(self: *const Document, id_: ?[]const u8) ?*Element {
const id = id_ orelse return null; const id = id_ orelse return null;
return self._elements_by_id.get(id); return self._elements_by_id.get(id);
@@ -317,6 +327,24 @@ pub fn importNode(_: *const Document, node: *Node, deep_: ?bool, page: *Page) !*
return node.cloneNode(deep_, page); return node.cloneNode(deep_, page);
} }
pub fn append(self: *Document, nodes: []const Node.NodeOrText, page: *Page) !void {
const parent = self.asNode();
for (nodes) |node_or_text| {
const child = try node_or_text.toNode(page);
_ = try parent.appendChild(child, page);
}
}
pub fn prepend(self: *Document, nodes: []const Node.NodeOrText, page: *Page) !void {
const parent = self.asNode();
var i = nodes.len;
while (i > 0) {
i -= 1;
const child = try nodes[i].toNode(page);
_ = try parent.insertBefore(child, parent.firstChild(), page);
}
}
const ReadyState = enum { const ReadyState = enum {
loading, loading,
interactive, interactive,
@@ -360,6 +388,7 @@ pub const JsApi = struct {
pub const createDocumentFragment = bridge.function(Document.createDocumentFragment, .{}); pub const createDocumentFragment = bridge.function(Document.createDocumentFragment, .{});
pub const createComment = bridge.function(Document.createComment, .{}); pub const createComment = bridge.function(Document.createComment, .{});
pub const createTextNode = bridge.function(Document.createTextNode, .{}); pub const createTextNode = bridge.function(Document.createTextNode, .{});
pub const createAttribute = bridge.function(Document.createAttribute, .{ .dom_exception = true });
pub const createCDATASection = bridge.function(Document.createCDATASection, .{ .dom_exception = true }); pub const createCDATASection = bridge.function(Document.createCDATASection, .{ .dom_exception = true });
pub const createRange = bridge.function(Document.createRange, .{}); pub const createRange = bridge.function(Document.createRange, .{});
pub const createEvent = bridge.function(Document.createEvent, .{ .dom_exception = true }); pub const createEvent = bridge.function(Document.createEvent, .{ .dom_exception = true });
@@ -373,6 +402,8 @@ pub const JsApi = struct {
pub const getElementsByName = bridge.function(Document.getElementsByName, .{}); pub const getElementsByName = bridge.function(Document.getElementsByName, .{});
pub const adoptNode = bridge.function(Document.adoptNode, .{ .dom_exception = true }); pub const adoptNode = bridge.function(Document.adoptNode, .{ .dom_exception = true });
pub const importNode = bridge.function(Document.importNode, .{ .dom_exception = true }); pub const importNode = bridge.function(Document.importNode, .{ .dom_exception = true });
pub const append = bridge.function(Document.append, .{});
pub const prepend = bridge.function(Document.prepend, .{});
pub const defaultView = bridge.accessor(struct { pub const defaultView = bridge.accessor(struct {
fn defaultView(_: *const Document, page: *Page) *@import("Window.zig") { fn defaultView(_: *const Document, page: *Page) *@import("Window.zig") {

View File

@@ -343,6 +343,14 @@ pub fn setId(self: *Element, value: []const u8, page: *Page) !void {
return self.setAttributeSafe("id", value, page); return self.setAttributeSafe("id", value, page);
} }
pub fn getDir(self: *const Element) []const u8 {
return self.getAttributeSafe("dir") orelse "";
}
pub fn setDir(self: *Element, value: []const u8, page: *Page) !void {
return self.setAttributeSafe("dir", value, page);
}
pub fn getClassName(self: *const Element) []const u8 { pub fn getClassName(self: *const Element) []const u8 {
return self.getAttributeSafe("class") orelse ""; return self.getAttributeSafe("class") orelse "";
} }
@@ -388,6 +396,7 @@ pub fn getAttributeNode(self: *Element, name: []const u8, page: *Page) !?*Attrib
} }
pub fn setAttribute(self: *Element, name: []const u8, value: []const u8, page: *Page) !void { pub fn setAttribute(self: *Element, name: []const u8, value: []const u8, page: *Page) !void {
try Attribute.validateAttributeName(name);
const attributes = try self.getOrCreateAttributeList(page); const attributes = try self.getOrCreateAttributeList(page);
_ = try attributes.put(name, value, self, page); _ = try attributes.put(name, value, self, page);
} }
@@ -503,6 +512,7 @@ pub fn removeAttribute(self: *Element, name: []const u8, page: *Page) !void {
} }
pub fn toggleAttribute(self: *Element, name: []const u8, force: ?bool, page: *Page) !bool { pub fn toggleAttribute(self: *Element, name: []const u8, force: ?bool, page: *Page) !bool {
try Attribute.validateAttributeName(name);
const has = try self.hasAttribute(name, page); const has = try self.hasAttribute(name, page);
const should_add = force orelse !has; const should_add = force orelse !has;
@@ -647,6 +657,27 @@ pub fn prepend(self: *Element, nodes: []const Node.NodeOrText, page: *Page) !voi
} }
} }
pub fn before(self: *Element, nodes: []const Node.NodeOrText, page: *Page) !void {
const node = self.asNode();
const parent = node.parentNode() orelse return;
for (nodes) |node_or_text| {
const child = try node_or_text.toNode(page);
_ = try parent.insertBefore(child, node, page);
}
}
pub fn after(self: *Element, nodes: []const Node.NodeOrText, page: *Page) !void {
const node = self.asNode();
const parent = node.parentNode() orelse return;
const next = node.nextSibling();
for (nodes) |node_or_text| {
const child = try node_or_text.toNode(page);
_ = try parent.insertBefore(child, next, page);
}
}
pub fn firstElementChild(self: *Element) ?*Element { pub fn firstElementChild(self: *Element) ?*Element {
var maybe_child = self.asNode().firstChild(); var maybe_child = self.asNode().firstChild();
while (maybe_child) |child| { while (maybe_child) |child| {
@@ -946,6 +977,10 @@ pub fn cloneElement(self: *Element, deep: bool, page: *Page) !*Node {
return node; return node;
} }
pub fn scrollIntoViewIfNeeded(_: *const Element, center_if_needed: ?bool) void {
_ = center_if_needed;
}
pub fn format(self: *Element, writer: *std.Io.Writer) !void { pub fn format(self: *Element, writer: *std.Io.Writer) !void {
try writer.writeByte('<'); try writer.writeByte('<');
try writer.writeAll(self.getTagNameDump()); try writer.writeAll(self.getTagNameDump());
@@ -1136,6 +1171,7 @@ pub const JsApi = struct {
pub const localName = bridge.accessor(Element.getLocalName, null, .{}); pub const localName = bridge.accessor(Element.getLocalName, null, .{});
pub const id = bridge.accessor(Element.getId, Element.setId, .{}); pub const id = bridge.accessor(Element.getId, Element.setId, .{});
pub const dir = bridge.accessor(Element.getDir, Element.setDir, .{});
pub const className = bridge.accessor(Element.getClassName, Element.setClassName, .{}); pub const className = bridge.accessor(Element.getClassName, Element.setClassName, .{});
pub const classList = bridge.accessor(Element.getClassList, null, .{}); pub const classList = bridge.accessor(Element.getClassList, null, .{});
pub const dataset = bridge.accessor(Element.getDataset, null, .{}); pub const dataset = bridge.accessor(Element.getDataset, null, .{});
@@ -1145,10 +1181,10 @@ pub const JsApi = struct {
pub const hasAttributes = bridge.function(Element.hasAttributes, .{}); pub const hasAttributes = bridge.function(Element.hasAttributes, .{});
pub const getAttribute = bridge.function(Element.getAttribute, .{}); pub const getAttribute = bridge.function(Element.getAttribute, .{});
pub const getAttributeNode = bridge.function(Element.getAttributeNode, .{}); pub const getAttributeNode = bridge.function(Element.getAttributeNode, .{});
pub const setAttribute = bridge.function(Element.setAttribute, .{}); pub const setAttribute = bridge.function(Element.setAttribute, .{ .dom_exception = true });
pub const setAttributeNode = bridge.function(Element.setAttributeNode, .{}); pub const setAttributeNode = bridge.function(Element.setAttributeNode, .{});
pub const removeAttribute = bridge.function(Element.removeAttribute, .{}); pub const removeAttribute = bridge.function(Element.removeAttribute, .{});
pub const toggleAttribute = bridge.function(Element.toggleAttribute, .{}); pub const toggleAttribute = bridge.function(Element.toggleAttribute, .{ .dom_exception = true });
pub const getAttributeNames = bridge.function(Element.getAttributeNames, .{}); pub const getAttributeNames = bridge.function(Element.getAttributeNames, .{});
pub const removeAttributeNode = bridge.function(Element.removeAttributeNode, .{ .dom_exception = true }); pub const removeAttributeNode = bridge.function(Element.removeAttributeNode, .{ .dom_exception = true });
pub const shadowRoot = bridge.accessor(Element.getShadowRoot, null, .{}); pub const shadowRoot = bridge.accessor(Element.getShadowRoot, null, .{});
@@ -1167,6 +1203,8 @@ pub const JsApi = struct {
pub const remove = bridge.function(Element.remove, .{}); pub const remove = bridge.function(Element.remove, .{});
pub const append = bridge.function(Element.append, .{}); pub const append = bridge.function(Element.append, .{});
pub const prepend = bridge.function(Element.prepend, .{}); pub const prepend = bridge.function(Element.prepend, .{});
pub const before = bridge.function(Element.before, .{});
pub const after = bridge.function(Element.after, .{});
pub const firstElementChild = bridge.accessor(Element.firstElementChild, null, .{}); pub const firstElementChild = bridge.accessor(Element.firstElementChild, null, .{});
pub const lastElementChild = bridge.accessor(Element.lastElementChild, null, .{}); pub const lastElementChild = bridge.accessor(Element.lastElementChild, null, .{});
pub const nextElementSibling = bridge.accessor(Element.nextElementSibling, null, .{}); pub const nextElementSibling = bridge.accessor(Element.nextElementSibling, null, .{});
@@ -1188,6 +1226,7 @@ pub const JsApi = struct {
pub const children = bridge.accessor(Element.getChildren, null, .{}); pub const children = bridge.accessor(Element.getChildren, null, .{});
pub const focus = bridge.function(Element.focus, .{}); pub const focus = bridge.function(Element.focus, .{});
pub const blur = bridge.function(Element.blur, .{}); pub const blur = bridge.function(Element.blur, .{});
pub const scrollIntoViewIfNeeded = bridge.function(Element.scrollIntoViewIfNeeded, .{});
}; };
pub const Build = struct { pub const Build = struct {

View File

@@ -111,6 +111,7 @@ pub const JsApi = struct {
pub const length = bridge.accessor(NodeList.length, null, .{}); pub const length = bridge.accessor(NodeList.length, null, .{});
pub const @"[]" = bridge.indexed(NodeList.getAtIndex, .{ .null_as_undefined = true }); pub const @"[]" = bridge.indexed(NodeList.getAtIndex, .{ .null_as_undefined = true });
pub const item = bridge.function(NodeList.getAtIndex, .{});
pub const keys = bridge.function(NodeList.keys, .{}); pub const keys = bridge.function(NodeList.keys, .{});
pub const values = bridge.function(NodeList.values, .{}); pub const values = bridge.function(NodeList.values, .{});
pub const entries = bridge.function(NodeList.entries, .{}); pub const entries = bridge.function(NodeList.entries, .{});

View File

@@ -343,6 +343,32 @@ fn shouldAddToIdMap(normalized_name: []const u8, element: *Element) bool {
return node.isConnected(); return node.isConnected();
} }
pub fn validateAttributeName(name: []const u8) !void {
if (name.len == 0) {
return error.InvalidCharacterError;
}
const first = name[0];
if ((first >= '0' and first <= '9') or first == '-' or first == '.') {
return error.InvalidCharacterError;
}
for (name) |c| {
if (c == 0 or c == '/' or c == '=' or c == '>' or std.ascii.isWhitespace(c)) {
return error.InvalidCharacterError;
}
const is_valid = (c >= 'a' and c <= 'z') or
(c >= 'A' and c <= 'Z') or
(c >= '0' and c <= '9') or
c == '_' or c == '-' or c == '.' or c == ':';
if (!is_valid) {
return error.InvalidCharacterError;
}
}
}
pub fn normalizeNameForLookup(name: []const u8, page: *Page) ![]const u8 { pub fn normalizeNameForLookup(name: []const u8, page: *Page) ![]const u8 {
if (!needsLowerCasing(name)) { if (!needsLowerCasing(name)) {
return name; return name;

View File

@@ -38,7 +38,8 @@ pub fn querySelector(root: *Node, input: []const u8, page: *Page) !?*Node.Elemen
if (first == .id) { if (first == .id) {
const el = page.getElementByIdFromNode(root, first.id) orelse continue; const el = page.getElementByIdFromNode(root, first.id) orelse continue;
// Check if the element is within the root subtree // Check if the element is within the root subtree
if (root.contains(el.asNode())) { const node = el.asNode();
if (node != root and root.contains(node)) {
return el; return el;
} }
continue; continue;