mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
Improve support for non-HTML namespace
This does a better job of tracking the implicit namespace based on the context. For example, when using DOMParser.parseFromString with an XML namespace, all subsequent elements will be in the XML namespace. Adds support for null namespace. Rather than defaulting to HTML, unknown namespaces now map to a special unknown type. We don't currently preserve the original namespace, but we're at least able to properly handle the casing in this case.
This commit is contained in:
@@ -1335,15 +1335,9 @@ pub fn adoptNodeTree(self: *Page, node: *Node, new_owner: *Document) !void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn createElement(self: *Page, ns_: ?[]const u8, name: []const u8, attribute_iterator: anytype) !*Node {
|
pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const u8, attribute_iterator: anytype) !*Node {
|
||||||
const namespace: Element.Namespace = blk: {
|
switch (namespace) {
|
||||||
const ns = ns_ orelse break :blk .html;
|
.html => {
|
||||||
if (std.mem.eql(u8, ns, "http://www.w3.org/2000/svg")) break :blk .svg;
|
|
||||||
if (std.mem.eql(u8, ns, "http://www.w3.org/1998/Math/MathML")) break :blk .mathml;
|
|
||||||
if (std.mem.eql(u8, ns, "http://www.w3.org/XML/1998/namespace")) break :blk .xml;
|
|
||||||
break :blk .html;
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (name.len) {
|
switch (name.len) {
|
||||||
1 => switch (name[0]) {
|
1 => switch (name[0]) {
|
||||||
'p' => return self.createHtmlElementT(
|
'p' => return self.createHtmlElementT(
|
||||||
@@ -1542,10 +1536,10 @@ pub fn createElement(self: *Page, ns_: ?[]const u8, name: []const u8, attribute_
|
|||||||
},
|
},
|
||||||
4 => switch (@as(u32, @bitCast(name[0..4].*))) {
|
4 => switch (@as(u32, @bitCast(name[0..4].*))) {
|
||||||
asUint("span") => return self.createHtmlElementT(
|
asUint("span") => return self.createHtmlElementT(
|
||||||
Element.Html.Span,
|
Element.Html.Generic,
|
||||||
namespace,
|
namespace,
|
||||||
attribute_iterator,
|
attribute_iterator,
|
||||||
.{ ._proto = undefined },
|
.{ ._proto = undefined, ._tag_name = String.init(undefined, "span", .{}) catch unreachable, ._tag = .span },
|
||||||
),
|
),
|
||||||
asUint("meta") => return self.createHtmlElementT(
|
asUint("meta") => return self.createHtmlElementT(
|
||||||
Element.Html.Meta,
|
Element.Html.Meta,
|
||||||
@@ -1889,22 +1883,6 @@ pub fn createElement(self: *Page, ns_: ?[]const u8, name: []const u8, attribute_
|
|||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (namespace == .svg) {
|
|
||||||
const tag_name = try String.init(self.arena, name, .{});
|
|
||||||
if (std.ascii.eqlIgnoreCase(name, "svg")) {
|
|
||||||
return self.createSvgElementT(Element.Svg, name, attribute_iterator, .{
|
|
||||||
._proto = undefined,
|
|
||||||
._type = .svg,
|
|
||||||
._tag_name = tag_name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other SVG elements (rect, circle, text, g, etc.)
|
|
||||||
const lower = std.ascii.lowerString(&self.buf, name);
|
|
||||||
const tag = std.meta.stringToEnum(Element.Tag, lower) orelse .unknown;
|
|
||||||
return self.createSvgElementT(Element.Svg.Generic, name, attribute_iterator, .{ ._proto = undefined, ._tag = tag });
|
|
||||||
}
|
|
||||||
|
|
||||||
const tag_name = try String.init(self.arena, name, .{});
|
const tag_name = try String.init(self.arena, name, .{});
|
||||||
|
|
||||||
// Check if this is a custom element (must have hyphen for HTML namespace)
|
// Check if this is a custom element (must have hyphen for HTML namespace)
|
||||||
@@ -1954,6 +1932,27 @@ pub fn createElement(self: *Page, ns_: ?[]const u8, name: []const u8, attribute_
|
|||||||
}
|
}
|
||||||
|
|
||||||
return self.createHtmlElementT(Element.Html.Unknown, namespace, attribute_iterator, .{ ._proto = undefined, ._tag_name = tag_name });
|
return self.createHtmlElementT(Element.Html.Unknown, namespace, attribute_iterator, .{ ._proto = undefined, ._tag_name = tag_name });
|
||||||
|
},
|
||||||
|
.svg => {
|
||||||
|
const tag_name = try String.init(self.arena, name, .{});
|
||||||
|
if (std.ascii.eqlIgnoreCase(name, "svg")) {
|
||||||
|
return self.createSvgElementT(Element.Svg, name, attribute_iterator, .{
|
||||||
|
._proto = undefined,
|
||||||
|
._type = .svg,
|
||||||
|
._tag_name = tag_name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other SVG elements (rect, circle, text, g, etc.)
|
||||||
|
const lower = std.ascii.lowerString(&self.buf, name);
|
||||||
|
const tag = std.meta.stringToEnum(Element.Tag, lower) orelse .unknown;
|
||||||
|
return self.createSvgElementT(Element.Svg.Generic, name, attribute_iterator, .{ ._proto = undefined, ._tag = tag });
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
const tag_name = try String.init(self.arena, name, .{});
|
||||||
|
return self.createHtmlElementT(Element.Html.Unknown, namespace, attribute_iterator, .{ ._proto = undefined, ._tag_name = tag_name });
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createHtmlElementT(self: *Page, comptime E: type, namespace: Element.Namespace, attribute_iterator: anytype, html_element: E) !*Node {
|
fn createHtmlElementT(self: *Page, comptime E: type, namespace: Element.Namespace, attribute_iterator: anytype, html_element: E) !*Node {
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ pub fn parseXML(self: *Parser, xml: []const u8) void {
|
|||||||
xml.len,
|
xml.len,
|
||||||
&self.container,
|
&self.container,
|
||||||
self,
|
self,
|
||||||
createElementCallback,
|
createXMLElementCallback,
|
||||||
getDataCallback,
|
getDataCallback,
|
||||||
appendCallback,
|
appendCallback,
|
||||||
parseErrorCallback,
|
parseErrorCallback,
|
||||||
@@ -225,17 +225,26 @@ fn _popCallback(self: *Parser, node: *Node) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn createElementCallback(ctx: *anyopaque, data: *anyopaque, qname: h5e.QualName, attributes: h5e.AttributeIterator) callconv(.c) ?*anyopaque {
|
fn createElementCallback(ctx: *anyopaque, data: *anyopaque, qname: h5e.QualName, attributes: h5e.AttributeIterator) callconv(.c) ?*anyopaque {
|
||||||
|
return _createElementCallbackWithDefaultnamespace(ctx, data, qname, attributes, .unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn createXMLElementCallback(ctx: *anyopaque, data: *anyopaque, qname: h5e.QualName, attributes: h5e.AttributeIterator) callconv(.c) ?*anyopaque {
|
||||||
|
return _createElementCallbackWithDefaultnamespace(ctx, data, qname, attributes, .xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _createElementCallbackWithDefaultnamespace(ctx: *anyopaque, data: *anyopaque, qname: h5e.QualName, attributes: h5e.AttributeIterator, default_namespace: Element.Namespace) ?*anyopaque {
|
||||||
const self: *Parser = @ptrCast(@alignCast(ctx));
|
const self: *Parser = @ptrCast(@alignCast(ctx));
|
||||||
return self._createElementCallback(data, qname, attributes) catch |err| {
|
return self._createElementCallback(data, qname, attributes, default_namespace) catch |err| {
|
||||||
self.err = .{ .err = err, .source = .create_element };
|
self.err = .{ .err = err, .source = .create_element };
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
fn _createElementCallback(self: *Parser, data: *anyopaque, qname: h5e.QualName, attributes: h5e.AttributeIterator) !*anyopaque {
|
fn _createElementCallback(self: *Parser, data: *anyopaque, qname: h5e.QualName, attributes: h5e.AttributeIterator, default_namespace: Element.Namespace) !*anyopaque {
|
||||||
const page = self.page;
|
const page = self.page;
|
||||||
const name = qname.local.slice();
|
const name = qname.local.slice();
|
||||||
const namespace = qname.ns.slice();
|
const namespace_string = qname.ns.slice();
|
||||||
const node = try page.createElement(namespace, name, attributes);
|
const namespace = if (namespace_string.len == 0) default_namespace else Element.Namespace.parse(namespace_string);
|
||||||
|
const node = try page.createElementNS(namespace, name, attributes);
|
||||||
|
|
||||||
const pn = try self.arena.create(ParsedNode);
|
const pn = try self.arena.create(ParsedNode);
|
||||||
pn.* = .{
|
pn.* = .{
|
||||||
|
|||||||
@@ -19,12 +19,13 @@
|
|||||||
testing.expectEqual('http://www.w3.org/XML/1998/namespace', xmlElement.namespaceURI);
|
testing.expectEqual('http://www.w3.org/XML/1998/namespace', xmlElement.namespaceURI);
|
||||||
|
|
||||||
const nullNsElement = document.createElementNS(null, 'span');
|
const nullNsElement = document.createElementNS(null, 'span');
|
||||||
testing.expectEqual('SPAN', nullNsElement.tagName);
|
testing.expectEqual('span', nullNsElement.tagName);
|
||||||
testing.expectEqual('http://www.w3.org/1999/xhtml', nullNsElement.namespaceURI);
|
testing.expectEqual(null, nullNsElement.namespaceURI);
|
||||||
|
|
||||||
const unknownNsElement = document.createElementNS('http://example.com/unknown', 'custom');
|
const unknownNsElement = document.createElementNS('http://example.com/unknown', 'custom');
|
||||||
testing.expectEqual('CUSTOM', unknownNsElement.tagName);
|
testing.expectEqual('custom', unknownNsElement.tagName);
|
||||||
testing.expectEqual('http://www.w3.org/1999/xhtml', unknownNsElement.namespaceURI);
|
// Should be http://example.com/unknown
|
||||||
|
testing.expectEqual('http://lightpanda.io/unsupported/namespace', unknownNsElement.namespaceURI);
|
||||||
|
|
||||||
const regularDiv = document.createElement('div');
|
const regularDiv = document.createElement('div');
|
||||||
testing.expectEqual('DIV', regularDiv.tagName);
|
testing.expectEqual('DIV', regularDiv.tagName);
|
||||||
@@ -36,5 +37,5 @@
|
|||||||
testing.expectEqual('te:ST', custom.tagName);
|
testing.expectEqual('te:ST', custom.tagName);
|
||||||
testing.expectEqual('te', custom.prefix);
|
testing.expectEqual('te', custom.prefix);
|
||||||
testing.expectEqual('ST', custom.localName);
|
testing.expectEqual('ST', custom.localName);
|
||||||
testing.expectEqual('http://www.w3.org/1999/xhtml', custom.namespaceURI); // Should be test
|
testing.expectEqual('http://lightpanda.io/unsupported/namespace', custom.namespaceURI); // Should be test
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -168,7 +168,7 @@
|
|||||||
const root = doc.documentElement;
|
const root = doc.documentElement;
|
||||||
testing.expectEqual(true, root !== null);
|
testing.expectEqual(true, root !== null);
|
||||||
// TODO: XML documents should preserve case, but we currently uppercase
|
// TODO: XML documents should preserve case, but we currently uppercase
|
||||||
testing.expectEqual('ROOT', root.tagName);
|
testing.expectEqual('root', root.tagName);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -206,10 +206,9 @@
|
|||||||
const doc = impl.createDocument('http://example.com', 'prefix:localName', null);
|
const doc = impl.createDocument('http://example.com', 'prefix:localName', null);
|
||||||
|
|
||||||
const root = doc.documentElement;
|
const root = doc.documentElement;
|
||||||
// TODO: XML documents should preserve case, but we currently uppercase
|
testing.expectEqual('prefix:localName', root.tagName);
|
||||||
testing.expectEqual('prefix:LOCALNAME', root.tagName);
|
// TODO: Custom namespaces are being replaced with an empty value
|
||||||
// TODO: Custom namespaces are being overridden to XHTML namespace
|
testing.expectEqual('http://lightpanda.io/unsupported/namespace', root.namespaceURI);
|
||||||
testing.expectEqual('http://www.w3.org/1999/xhtml', root.namespaceURI);
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -224,8 +223,7 @@
|
|||||||
doc.documentElement.appendChild(child);
|
doc.documentElement.appendChild(child);
|
||||||
|
|
||||||
testing.expectEqual(1, doc.documentElement.childNodes.length);
|
testing.expectEqual(1, doc.documentElement.childNodes.length);
|
||||||
// TODO: XML documents should preserve case, but we currently uppercase
|
testing.expectEqual('child', doc.documentElement.firstChild.tagName);
|
||||||
testing.expectEqual('CHILD', doc.documentElement.firstChild.tagName);
|
|
||||||
testing.expectEqual('Test', doc.documentElement.firstChild.textContent);
|
testing.expectEqual('Test', doc.documentElement.firstChild.textContent);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -364,14 +364,14 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (const mime of mimes) {
|
for (const mime of mimes) {
|
||||||
const doc = parser.parseFromString(sampleXML, "text/xml");
|
const doc = parser.parseFromString(sampleXML, mime);
|
||||||
const { firstChild: { childNodes, children: collection, tagName }, children } = doc;
|
const { firstChild: { childNodes, children: collection, tagName }, children } = doc;
|
||||||
// doc.
|
// doc.
|
||||||
testing.expectEqual(true, doc instanceof XMLDocument);
|
testing.expectEqual(true, doc instanceof XMLDocument);
|
||||||
testing.expectEqual(1, children.length);
|
testing.expectEqual(1, children.length);
|
||||||
// firstChild.
|
// firstChild.
|
||||||
// TODO: Modern browsers expect this in lowercase.
|
// TODO: Modern browsers expect this in lowercase.
|
||||||
testing.expectEqual("CATALOG", tagName);
|
testing.expectEqual("catalog", tagName);
|
||||||
testing.expectEqual(25, childNodes.length);
|
testing.expectEqual(25, childNodes.length);
|
||||||
testing.expectEqual(12, collection.length);
|
testing.expectEqual(12, collection.length);
|
||||||
// Check children of first child.
|
// Check children of first child.
|
||||||
@@ -379,12 +379,12 @@
|
|||||||
const {children: elements, id} = collection.item(i);
|
const {children: elements, id} = collection.item(i);
|
||||||
testing.expectEqual("bk" + (100 + i + 1), id);
|
testing.expectEqual("bk" + (100 + i + 1), id);
|
||||||
// TODO: Modern browsers expect these in lowercase.
|
// TODO: Modern browsers expect these in lowercase.
|
||||||
testing.expectEqual("AUTHOR", elements.item(0).tagName);
|
testing.expectEqual("author", elements.item(0).tagName);
|
||||||
testing.expectEqual("TITLE", elements.item(1).tagName);
|
testing.expectEqual("title", elements.item(1).tagName);
|
||||||
testing.expectEqual("GENRE", elements.item(2).tagName);
|
testing.expectEqual("genre", elements.item(2).tagName);
|
||||||
testing.expectEqual("PRICE", elements.item(3).tagName);
|
testing.expectEqual("price", elements.item(3).tagName);
|
||||||
testing.expectEqual("PUBLISH_DATE", elements.item(4).tagName);
|
testing.expectEqual("publish_date", elements.item(4).tagName);
|
||||||
testing.expectEqual("DESCRIPTION", elements.item(5).tagName);
|
testing.expectEqual("description", elements.item(5).tagName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,26 +57,26 @@ pub fn createHTMLDocument(_: *const DOMImplementation, title: ?[]const u8, page:
|
|||||||
_ = try document.asNode().appendChild(doctype.asNode(), page);
|
_ = try document.asNode().appendChild(doctype.asNode(), page);
|
||||||
}
|
}
|
||||||
|
|
||||||
const html_node = try page.createElement(null, "html", null);
|
const html_node = try page.createElementNS(.html, "html", null);
|
||||||
_ = try document.asNode().appendChild(html_node, page);
|
_ = try document.asNode().appendChild(html_node, page);
|
||||||
|
|
||||||
const head_node = try page.createElement(null, "head", null);
|
const head_node = try page.createElementNS(.html, "head", null);
|
||||||
_ = try html_node.appendChild(head_node, page);
|
_ = try html_node.appendChild(head_node, page);
|
||||||
|
|
||||||
if (title) |t| {
|
if (title) |t| {
|
||||||
const title_node = try page.createElement(null, "title", null);
|
const title_node = try page.createElementNS(.html, "title", null);
|
||||||
_ = try head_node.appendChild(title_node, page);
|
_ = try head_node.appendChild(title_node, page);
|
||||||
const text_node = try page.createTextNode(t);
|
const text_node = try page.createTextNode(t);
|
||||||
_ = try title_node.appendChild(text_node, page);
|
_ = try title_node.appendChild(text_node, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
const body_node = try page.createElement(null, "body", null);
|
const body_node = try page.createElementNS(.html, "body", null);
|
||||||
_ = try html_node.appendChild(body_node, page);
|
_ = try html_node.appendChild(body_node, page);
|
||||||
|
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn createDocument(_: *const DOMImplementation, namespace: ?[]const u8, qualified_name: ?[]const u8, doctype: ?*DocumentType, page: *Page) !*Document {
|
pub fn createDocument(_: *const DOMImplementation, namespace_: ?[]const u8, qualified_name: ?[]const u8, doctype: ?*DocumentType, page: *Page) !*Document {
|
||||||
// Create XML Document
|
// Create XML Document
|
||||||
const document = (try page._factory.document(Node.Document.XMLDocument{ ._proto = undefined })).asDocument();
|
const document = (try page._factory.document(Node.Document.XMLDocument{ ._proto = undefined })).asDocument();
|
||||||
|
|
||||||
@@ -88,7 +88,8 @@ pub fn createDocument(_: *const DOMImplementation, namespace: ?[]const u8, quali
|
|||||||
// Create and append root element if qualified_name provided
|
// Create and append root element if qualified_name provided
|
||||||
if (qualified_name) |qname| {
|
if (qualified_name) |qname| {
|
||||||
if (qname.len > 0) {
|
if (qname.len > 0) {
|
||||||
const root = try page.createElement(namespace, qname, null);
|
const namespace = if (namespace_) |ns| Node.Element.Namespace.parse(ns) else .xml;
|
||||||
|
const root = try page.createElementNS(namespace, qname, null);
|
||||||
_ = try document.asNode().appendChild(root, page);
|
_ = try document.asNode().appendChild(root, page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,14 @@ const CreateElementOptions = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn createElement(self: *Document, name: []const u8, options_: ?CreateElementOptions, page: *Page) !*Element {
|
pub fn createElement(self: *Document, name: []const u8, options_: ?CreateElementOptions, page: *Page) !*Element {
|
||||||
const node = try page.createElement(null, name, null);
|
const namespace: Element.Namespace = blk: {
|
||||||
|
if (self._type == .xml) {
|
||||||
|
@branchHint(.unlikely);
|
||||||
|
break :blk .xml;
|
||||||
|
}
|
||||||
|
break :blk .html;
|
||||||
|
};
|
||||||
|
const node = try page.createElementNS(namespace, name, null);
|
||||||
const element = node.as(Element);
|
const element = node.as(Element);
|
||||||
|
|
||||||
// Track owner document if it's not the main document
|
// Track owner document if it's not the main document
|
||||||
@@ -142,7 +149,7 @@ pub fn createElement(self: *Document, name: []const u8, options_: ?CreateElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn createElementNS(self: *Document, namespace: ?[]const u8, name: []const u8, page: *Page) !*Element {
|
pub fn createElementNS(self: *Document, namespace: ?[]const u8, name: []const u8, page: *Page) !*Element {
|
||||||
const node = try page.createElement(namespace, name, null);
|
const node = try page.createElementNS(Element.Namespace.parse(namespace), name, null);
|
||||||
|
|
||||||
// Track owner document if it's not the main document
|
// Track owner document if it's not the main document
|
||||||
if (self != page.document) {
|
if (self != page.document) {
|
||||||
|
|||||||
@@ -53,15 +53,41 @@ pub const Namespace = enum(u8) {
|
|||||||
svg,
|
svg,
|
||||||
mathml,
|
mathml,
|
||||||
xml,
|
xml,
|
||||||
|
// We should keep the original value, but don't. If this becomes important
|
||||||
|
// consider storing it in a page lookup, like `_element_class_lists`, rather
|
||||||
|
// that adding a slice directly here (directly in every element).
|
||||||
|
unknown,
|
||||||
|
null,
|
||||||
|
|
||||||
pub fn toUri(self: Namespace) []const u8 {
|
pub fn toUri(self: Namespace) ?[]const u8 {
|
||||||
return switch (self) {
|
return switch (self) {
|
||||||
.html => "http://www.w3.org/1999/xhtml",
|
.html => "http://www.w3.org/1999/xhtml",
|
||||||
.svg => "http://www.w3.org/2000/svg",
|
.svg => "http://www.w3.org/2000/svg",
|
||||||
.mathml => "http://www.w3.org/1998/Math/MathML",
|
.mathml => "http://www.w3.org/1998/Math/MathML",
|
||||||
.xml => "http://www.w3.org/XML/1998/namespace",
|
.xml => "http://www.w3.org/XML/1998/namespace",
|
||||||
|
.unknown => "http://lightpanda.io/unsupported/namespace",
|
||||||
|
.null => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse(namespace_: ?[]const u8) Namespace {
|
||||||
|
const namespace = namespace_ orelse return .null;
|
||||||
|
if (namespace.len == "http://www.w3.org/1999/xhtml".len) {
|
||||||
|
// Common case, avoid the string comparion. Recklessly
|
||||||
|
@branchHint(.likely);
|
||||||
|
return .html;
|
||||||
|
}
|
||||||
|
if (std.mem.eql(u8, namespace, "http://www.w3.org/XML/1998/namespace")) {
|
||||||
|
return .xml;
|
||||||
|
}
|
||||||
|
if (std.mem.eql(u8, namespace, "http://www.w3.org/2000/svg")) {
|
||||||
|
return .svg;
|
||||||
|
}
|
||||||
|
if (std.mem.eql(u8, namespace, "http://www.w3.org/1998/Math/MathML")) {
|
||||||
|
return .mathml;
|
||||||
|
}
|
||||||
|
return .unknown;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_type: Type,
|
_type: Type,
|
||||||
@@ -211,13 +237,8 @@ pub fn getTagNameLower(self: *const Element) []const u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn getTagNameSpec(self: *const Element, buf: []u8) []const u8 {
|
pub fn getTagNameSpec(self: *const Element, buf: []u8) []const u8 {
|
||||||
switch (self._type) {
|
return switch (self._type) {
|
||||||
.html => |he| switch (he._type) {
|
.html => |he| switch (he._type) {
|
||||||
.custom => |e| {
|
|
||||||
@branchHint(.unlikely);
|
|
||||||
return upperTagName(&e._tag_name, buf);
|
|
||||||
},
|
|
||||||
else => return switch (he._type) {
|
|
||||||
.anchor => "A",
|
.anchor => "A",
|
||||||
.body => "BODY",
|
.body => "BODY",
|
||||||
.br => "BR",
|
.br => "BR",
|
||||||
@@ -259,12 +280,11 @@ pub fn getTagNameSpec(self: *const Element, buf: []u8) []const u8 {
|
|||||||
.ul => "UL",
|
.ul => "UL",
|
||||||
.unknown => |e| switch (self._namespace) {
|
.unknown => |e| switch (self._namespace) {
|
||||||
.html => upperTagName(&e._tag_name, buf),
|
.html => upperTagName(&e._tag_name, buf),
|
||||||
.svg, .xml, .mathml => return e._tag_name.str(),
|
.svg, .xml, .mathml, .unknown, .null => e._tag_name.str(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
.svg => |svg| svg._tag_name.str(),
|
||||||
.svg => |svg| return svg._tag_name.str(),
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getTagNameDump(self: *const Element) []const u8 {
|
pub fn getTagNameDump(self: *const Element) []const u8 {
|
||||||
@@ -274,7 +294,7 @@ pub fn getTagNameDump(self: *const Element) []const u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getNamespaceURI(self: *const Element) []const u8 {
|
pub fn getNamespaceURI(self: *const Element) ?[]const u8 {
|
||||||
return self._namespace.toUri();
|
return self._namespace.toUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -996,9 +1016,7 @@ pub fn getElementsByClassName(self: *Element, class_name: []const u8, page: *Pag
|
|||||||
|
|
||||||
pub fn cloneElement(self: *Element, deep: bool, page: *Page) !*Node {
|
pub fn cloneElement(self: *Element, deep: bool, page: *Page) !*Node {
|
||||||
const tag_name = self.getTagNameDump();
|
const tag_name = self.getTagNameDump();
|
||||||
const namespace_uri = self.getNamespaceURI();
|
const node = try page.createElementNS(self._namespace, tag_name, self._attributes);
|
||||||
|
|
||||||
const node = try page.createElement(namespace_uri, tag_name, self._attributes);
|
|
||||||
|
|
||||||
// Allow element-specific types to copy their runtime state
|
// Allow element-specific types to copy their runtime state
|
||||||
_ = Element.Build.call(node.as(Element), "cloned", .{ self, node.as(Element), page }) catch |err| {
|
_ = Element.Build.call(node.as(Element), "cloned", .{ self, node.as(Element), page }) catch |err| {
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ pub fn setTitle(self: *HTMLDocument, title: []const u8, page: *Page) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No title element found, create one
|
// No title element found, create one
|
||||||
const title_node = try page.createElement(null, "title", null);
|
const title_node = try page.createElementNS(.html, "title", null);
|
||||||
const title_element = title_node.as(Element);
|
const title_element = title_node.as(Element);
|
||||||
|
|
||||||
// Only add text if non-empty
|
// Only add text if non-empty
|
||||||
|
|||||||
@@ -443,9 +443,9 @@ pub fn createContextualFragment(self: *const Range, html: []const u8, page: *Pag
|
|||||||
// Create a temporary element of the same type as the context for parsing
|
// Create a temporary element of the same type as the context for parsing
|
||||||
// This preserves the parsing context without modifying the original node
|
// This preserves the parsing context without modifying the original node
|
||||||
const temp_node = if (context_node.is(Node.Element)) |el|
|
const temp_node = if (context_node.is(Node.Element)) |el|
|
||||||
try page.createElement(el._namespace.toUri(), el.getTagNameLower(), null)
|
try page.createElementNS(el._namespace, el.getTagNameLower(), null)
|
||||||
else
|
else
|
||||||
try page.createElement(null, "div", null);
|
try page.createElementNS(.html, "div", null);
|
||||||
|
|
||||||
try page.parseHtmlAsChildren(temp_node, html);
|
try page.parseHtmlAsChildren(temp_node, html);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const Image = @This();
|
|||||||
_proto: *HtmlElement,
|
_proto: *HtmlElement,
|
||||||
|
|
||||||
pub fn constructor(w_: ?u32, h_: ?u32, page: *Page) !*Image {
|
pub fn constructor(w_: ?u32, h_: ?u32, page: *Page) !*Image {
|
||||||
const node = try page.createElement(null, "img", null);
|
const node = try page.createElementNS(.html, "img", null);
|
||||||
const el = node.as(Element);
|
const el = node.as(Element);
|
||||||
|
|
||||||
if (w_) |w| blk: {
|
if (w_) |w| blk: {
|
||||||
|
|||||||
Reference in New Issue
Block a user