mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 01:25:53 -04:00
feat(webapi): implement Node.lookupPrefix
Implement the Node.lookupPrefix(namespace) method per the WHATWG DOM "locate a namespace prefix" algorithm: match the element's own namespace and prefix, then scan its xmlns: attribute declarations for one whose value is the namespace, then recurse to the parent element. This is the inverse of the existing lookupNamespaceURI and completes the namespace-introspection trio alongside isDefaultNamespace.
This commit is contained in:
46
src/browser/tests/node/lookup_prefix.html
Normal file
46
src/browser/tests/node/lookup_prefix.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<div id="host" xmlns:foo="http://example.com/foo" xmlns:bar="http://example.com/bar">
|
||||
<span id="child"></span>
|
||||
</div>
|
||||
|
||||
<script id="lookupPrefix">
|
||||
{
|
||||
const host = document.getElementById('host');
|
||||
|
||||
// Resolves a prefix declared via an xmlns: attribute on the element.
|
||||
testing.expectEqual('foo', host.lookupPrefix('http://example.com/foo'));
|
||||
testing.expectEqual('bar', host.lookupPrefix('http://example.com/bar'));
|
||||
|
||||
// A descendant inherits the declaration by walking up to the ancestor.
|
||||
const child = document.getElementById('child');
|
||||
testing.expectEqual('foo', child.lookupPrefix('http://example.com/foo'));
|
||||
|
||||
// Unknown namespace returns null.
|
||||
testing.expectEqual(null, host.lookupPrefix('http://example.com/missing'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="lookupPrefix-null-empty">
|
||||
{
|
||||
const host = document.getElementById('host');
|
||||
|
||||
// null or empty namespace always returns null.
|
||||
testing.expectEqual(null, host.lookupPrefix(null));
|
||||
testing.expectEqual(null, host.lookupPrefix(''));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="lookupPrefix-own-prefix">
|
||||
{
|
||||
// An element whose own prefix declares the namespace returns that prefix.
|
||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg:rect');
|
||||
testing.expectEqual('svg', svg.prefix);
|
||||
testing.expectEqual('svg', svg.lookupPrefix('http://www.w3.org/2000/svg'));
|
||||
|
||||
// Detached element with no matching declaration returns null.
|
||||
const div = document.createElement('div');
|
||||
testing.expectEqual(null, div.lookupPrefix('http://example.com/foo'));
|
||||
}
|
||||
</script>
|
||||
@@ -422,6 +422,34 @@ pub fn lookupNamespaceURIForElement(self: *Element, prefix: ?[]const u8, frame:
|
||||
return parent.lookupNamespaceURIForElement(prefix, frame);
|
||||
}
|
||||
|
||||
// Locate a namespace prefix: the inverse of lookupNamespaceURIForElement.
|
||||
// Given a namespace URI, find the prefix that declares it.
|
||||
pub fn lookupPrefixForElement(self: *Element, namespace: []const u8, frame: *Frame) ?[]const u8 {
|
||||
// Step 1: element's own namespace/prefix
|
||||
if (self.getNamespaceUri(frame)) |ns_uri| {
|
||||
if (self._prefix()) |el_prefix| {
|
||||
if (std.mem.eql(u8, ns_uri, namespace)) {
|
||||
return el_prefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: search xmlns: attribute declarations for one whose value is the namespace
|
||||
if (self._attributes) |attrs| {
|
||||
var iter = attrs.iterator();
|
||||
while (iter.next()) |entry| {
|
||||
const name = entry._name.str();
|
||||
if (std.mem.startsWith(u8, name, "xmlns:") and std.mem.eql(u8, entry._value.str(), namespace)) {
|
||||
return name["xmlns:".len..];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: recurse to parent element
|
||||
const parent = self.asNode().parentElement() orelse return null;
|
||||
return parent.lookupPrefixForElement(namespace, frame);
|
||||
}
|
||||
|
||||
fn _prefix(self: *const Element) ?[]const u8 {
|
||||
const name = self.getTagNameLower();
|
||||
if (std.mem.indexOfPos(u8, name, 0, ":")) |pos| {
|
||||
|
||||
@@ -379,6 +379,28 @@ pub fn lookupNamespaceURI(self: *Node, prefix_arg: ?[]const u8, frame: *Frame) ?
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookupPrefix(self: *Node, namespace_arg: ?[]const u8, frame: *Frame) ?[]const u8 {
|
||||
const namespace = namespace_arg orelse return null;
|
||||
if (namespace.len == 0) return null;
|
||||
|
||||
switch (self._type) {
|
||||
.element => |el| return el.lookupPrefixForElement(namespace, frame),
|
||||
.document => |doc| {
|
||||
const de = doc.getDocumentElement() orelse return null;
|
||||
return de.lookupPrefixForElement(namespace, frame);
|
||||
},
|
||||
.document_type, .document_fragment => return null,
|
||||
.attribute => |attr| {
|
||||
const owner = attr.getOwnerElement() orelse return null;
|
||||
return owner.lookupPrefixForElement(namespace, frame);
|
||||
},
|
||||
.cdata => {
|
||||
const parent = self.parentElement() orelse return null;
|
||||
return parent.lookupPrefixForElement(namespace, frame);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isDefaultNamespace(self: *Node, namespace_arg: ?[]const u8, frame: *Frame) bool {
|
||||
const namespace: ?[]const u8 = if (namespace_arg) |ns| (if (ns.len == 0) null else ns) else null;
|
||||
const default_ns = self.lookupNamespaceURI(null, frame);
|
||||
@@ -1197,6 +1219,7 @@ pub const JsApi = struct {
|
||||
pub const getRootNode = bridge.function(Node.getRootNode, .{});
|
||||
pub const isEqualNode = bridge.function(Node.isEqualNode, .{});
|
||||
pub const lookupNamespaceURI = bridge.function(Node.lookupNamespaceURI, .{});
|
||||
pub const lookupPrefix = bridge.function(Node.lookupPrefix, .{});
|
||||
pub const isDefaultNamespace = bridge.function(Node.isDefaultNamespace, .{});
|
||||
|
||||
fn _baseURI(_: *Node, frame: *const Frame) []const u8 {
|
||||
|
||||
Reference in New Issue
Block a user