mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1613 from egrs/lookup-namespace-uri
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
implement Node.lookupNamespaceURI() and isDefaultNamespace()
This commit is contained in:
@@ -111,6 +111,7 @@ _element_shadow_roots: Element.ShadowRootLookup = .empty,
|
||||
_node_owner_documents: Node.OwnerDocumentLookup = .empty,
|
||||
_element_assigned_slots: Element.AssignedSlotLookup = .empty,
|
||||
_element_scroll_positions: Element.ScrollPositionLookup = .empty,
|
||||
_element_namespace_uris: Element.NamespaceUriLookup = .empty,
|
||||
|
||||
/// Lazily-created inline event listeners (or listeners provided as attributes).
|
||||
/// Avoids bloating all elements with extra function fields for rare usage.
|
||||
|
||||
@@ -160,6 +160,14 @@ pub fn createElementNS(self: *Document, namespace: ?[]const u8, name: []const u8
|
||||
const normalized_name = if (ns == .html) std.ascii.lowerString(&page.buf, name) else name;
|
||||
const node = try page.createElementNS(ns, normalized_name, null);
|
||||
|
||||
// Store original URI for unknown namespaces so lookupNamespaceURI can return it
|
||||
if (ns == .unknown) {
|
||||
if (namespace) |uri| {
|
||||
const duped = try page.dupeString(uri);
|
||||
try page._element_namespace_uris.put(page.arena, node.as(Element), duped);
|
||||
}
|
||||
}
|
||||
|
||||
// Track owner document if it's not the main document
|
||||
if (self != page.document) {
|
||||
try page.setNodeOwnerDocument(node, self);
|
||||
|
||||
@@ -49,6 +49,7 @@ pub const ClassListLookup = std.AutoHashMapUnmanaged(*Element, *collections.DOMT
|
||||
pub const RelListLookup = std.AutoHashMapUnmanaged(*Element, *collections.DOMTokenList);
|
||||
pub const ShadowRootLookup = std.AutoHashMapUnmanaged(*Element, *ShadowRoot);
|
||||
pub const AssignedSlotLookup = std.AutoHashMapUnmanaged(*Element, *Html.Slot);
|
||||
pub const NamespaceUriLookup = std.AutoHashMapUnmanaged(*Element, []const u8);
|
||||
|
||||
pub const ScrollPosition = struct {
|
||||
x: u32 = 0,
|
||||
@@ -364,6 +365,64 @@ pub fn getNamespaceURI(self: *const Element) ?[]const u8 {
|
||||
return self._namespace.toUri();
|
||||
}
|
||||
|
||||
pub fn getNamespaceUri(self: *Element, page: *Page) ?[]const u8 {
|
||||
if (self._namespace != .unknown) return self._namespace.toUri();
|
||||
return page._element_namespace_uris.get(self);
|
||||
}
|
||||
|
||||
pub fn lookupNamespaceURIForElement(self: *Element, prefix: ?[]const u8, page: *Page) ?[]const u8 {
|
||||
// Hardcoded reserved prefixes
|
||||
if (prefix) |p| {
|
||||
if (std.mem.eql(u8, p, "xml")) return "http://www.w3.org/XML/1998/namespace";
|
||||
if (std.mem.eql(u8, p, "xmlns")) return "http://www.w3.org/2000/xmlns/";
|
||||
}
|
||||
|
||||
// Step 1: check element's own namespace/prefix
|
||||
if (self.getNamespaceUri(page)) |ns_uri| {
|
||||
const el_prefix = self._prefix();
|
||||
const match = if (prefix == null and el_prefix == null)
|
||||
true
|
||||
else if (prefix != null and el_prefix != null)
|
||||
std.mem.eql(u8, prefix.?, el_prefix.?)
|
||||
else
|
||||
false;
|
||||
if (match) return ns_uri;
|
||||
}
|
||||
|
||||
// Step 2: search xmlns attributes
|
||||
if (self._attributes) |attrs| {
|
||||
var iter = attrs.iterator();
|
||||
while (iter.next()) |entry| {
|
||||
if (prefix == null) {
|
||||
if (entry._name.eql(comptime .wrap("xmlns"))) {
|
||||
const val = entry._value.str();
|
||||
return if (val.len == 0) null else val;
|
||||
}
|
||||
} else {
|
||||
const name = entry._name.str();
|
||||
if (std.mem.startsWith(u8, name, "xmlns:")) {
|
||||
if (std.mem.eql(u8, name["xmlns:".len..], prefix.?)) {
|
||||
const val = entry._value.str();
|
||||
return if (val.len == 0) null else val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: recurse to parent element
|
||||
const parent = self.asNode().parentElement() orelse return null;
|
||||
return parent.lookupNamespaceURIForElement(prefix, page);
|
||||
}
|
||||
|
||||
fn _prefix(self: *const Element) ?[]const u8 {
|
||||
const name = self.getTagNameLower();
|
||||
if (std.mem.indexOfPos(u8, name, 0, ":")) |pos| {
|
||||
return name[0..pos];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getLocalName(self: *Element) []const u8 {
|
||||
const name = self.getTagNameLower();
|
||||
if (std.mem.indexOfPos(u8, name, 0, ":")) |pos| {
|
||||
@@ -534,17 +593,26 @@ pub fn setAttributeNS(
|
||||
value: String,
|
||||
page: *Page,
|
||||
) !void {
|
||||
if (maybe_namespace) |namespace| {
|
||||
const attr_name = if (maybe_namespace) |namespace| blk: {
|
||||
// For xmlns namespace, store the full qualified name (e.g. "xmlns:bar")
|
||||
// so lookupNamespaceURI can find namespace declarations.
|
||||
if (std.mem.eql(u8, namespace, "http://www.w3.org/2000/xmlns/")) {
|
||||
break :blk qualified_name;
|
||||
}
|
||||
if (!std.mem.eql(u8, namespace, "http://www.w3.org/1999/xhtml")) {
|
||||
log.warn(.not_implemented, "Element.setAttributeNS", .{ .namespace = namespace });
|
||||
}
|
||||
}
|
||||
|
||||
const local_name = if (std.mem.indexOfScalarPos(u8, qualified_name, 0, ':')) |idx|
|
||||
qualified_name[idx + 1 ..]
|
||||
else
|
||||
qualified_name;
|
||||
return self.setAttribute(.wrap(local_name), value, page);
|
||||
break :blk if (std.mem.indexOfScalarPos(u8, qualified_name, 0, ':')) |idx|
|
||||
qualified_name[idx + 1 ..]
|
||||
else
|
||||
qualified_name;
|
||||
} else blk: {
|
||||
break :blk if (std.mem.indexOfScalarPos(u8, qualified_name, 0, ':')) |idx|
|
||||
qualified_name[idx + 1 ..]
|
||||
else
|
||||
qualified_name;
|
||||
};
|
||||
return self.setAttribute(.wrap(attr_name), value, page);
|
||||
}
|
||||
|
||||
pub fn setAttributeSafe(self: *Element, name: String, value: String, page: *Page) !void {
|
||||
@@ -1560,15 +1628,7 @@ pub const JsApi = struct {
|
||||
return buf.written();
|
||||
}
|
||||
|
||||
pub const prefix = bridge.accessor(_prefix, null, .{});
|
||||
fn _prefix(self: *Element) ?[]const u8 {
|
||||
const name = self.getTagNameLower();
|
||||
if (std.mem.indexOfPos(u8, name, 0, ":")) |pos| {
|
||||
return name[0..pos];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
pub const prefix = bridge.accessor(Element._prefix, null, .{});
|
||||
|
||||
pub const setAttribute = bridge.function(_setAttribute, .{ .dom_exception = true });
|
||||
fn _setAttribute(self: *Element, name: String, value: js.Value, page: *Page) !void {
|
||||
|
||||
@@ -338,6 +338,35 @@ pub fn getNodeType(self: *const Node) u8 {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn lookupNamespaceURI(self: *Node, prefix_arg: ?[]const u8, page: *Page) ?[]const u8 {
|
||||
const prefix: ?[]const u8 = if (prefix_arg) |p| (if (p.len == 0) null else p) else null;
|
||||
|
||||
switch (self._type) {
|
||||
.element => |el| return el.lookupNamespaceURIForElement(prefix, page),
|
||||
.document => |doc| {
|
||||
const de = doc.getDocumentElement() orelse return null;
|
||||
return de.lookupNamespaceURIForElement(prefix, page);
|
||||
},
|
||||
.document_type, .document_fragment => return null,
|
||||
.attribute => |attr| {
|
||||
const owner = attr.getOwnerElement() orelse return null;
|
||||
return owner.lookupNamespaceURIForElement(prefix, page);
|
||||
},
|
||||
.cdata => {
|
||||
const parent = self.parentElement() orelse return null;
|
||||
return parent.lookupNamespaceURIForElement(prefix, page);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isDefaultNamespace(self: *Node, namespace_arg: ?[]const u8, page: *Page) bool {
|
||||
const namespace: ?[]const u8 = if (namespace_arg) |ns| (if (ns.len == 0) null else ns) else null;
|
||||
const default_ns = self.lookupNamespaceURI(null, page);
|
||||
if (default_ns == null and namespace == null) return true;
|
||||
if (default_ns != null and namespace != null) return std.mem.eql(u8, default_ns.?, namespace.?);
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn isEqualNode(self: *Node, other: *Node) bool {
|
||||
if (self == other) {
|
||||
return true;
|
||||
@@ -1016,6 +1045,8 @@ pub const JsApi = struct {
|
||||
pub const compareDocumentPosition = bridge.function(Node.compareDocumentPosition, .{});
|
||||
pub const getRootNode = bridge.function(Node.getRootNode, .{});
|
||||
pub const isEqualNode = bridge.function(Node.isEqualNode, .{});
|
||||
pub const lookupNamespaceURI = bridge.function(Node.lookupNamespaceURI, .{});
|
||||
pub const isDefaultNamespace = bridge.function(Node.isDefaultNamespace, .{});
|
||||
|
||||
fn _baseURI(_: *Node, page: *const Page) []const u8 {
|
||||
return page.base();
|
||||
|
||||
Reference in New Issue
Block a user