mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
Merge pull request #1092 from lightpanda-io/nikneym/insert-adjacent-html
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
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (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
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Support `Element#insertAdjacentHTML`
This commit is contained in:
@@ -231,6 +231,85 @@ pub const Element = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses the given `input` string and inserts its children to an element at given `position`.
|
||||||
|
/// https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
|
||||||
|
///
|
||||||
|
/// TODO: Support for XML parsing and `TrustedHTML` instances.
|
||||||
|
pub fn _insertAdjacentHTML(self: *parser.Element, position: []const u8, input: []const u8) !void {
|
||||||
|
const self_node = parser.elementToNode(self);
|
||||||
|
const doc = parser.nodeOwnerDocument(self_node) orelse {
|
||||||
|
return parser.DOMError.WrongDocument;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse the fragment.
|
||||||
|
// Should return error.Syntax on fail?
|
||||||
|
const fragment = try parser.documentParseFragmentFromStr(doc, input);
|
||||||
|
const fragment_node = parser.documentFragmentToNode(fragment);
|
||||||
|
|
||||||
|
// We always get it wrapped like so:
|
||||||
|
// <html><head></head><body>{ ... }</body></html>
|
||||||
|
// None of the following can be null.
|
||||||
|
const maybe_html = parser.nodeFirstChild(fragment_node);
|
||||||
|
std.debug.assert(maybe_html != null);
|
||||||
|
const html = maybe_html orelse return;
|
||||||
|
|
||||||
|
const maybe_body = parser.nodeLastChild(html);
|
||||||
|
std.debug.assert(maybe_body != null);
|
||||||
|
const body = maybe_body orelse return;
|
||||||
|
|
||||||
|
const children = try parser.nodeGetChildNodes(body);
|
||||||
|
|
||||||
|
// * `target_node` is `*Node` (where we actually insert),
|
||||||
|
// * `prev_node` is `?*Node`.
|
||||||
|
const target_node, const prev_node = blk: {
|
||||||
|
// Prefer case-sensitive match.
|
||||||
|
// "beforeend" was the most common case in my tests; we might adjust the order
|
||||||
|
// depending on which ones websites prefer most.
|
||||||
|
if (std.mem.eql(u8, position, "beforeend")) {
|
||||||
|
break :blk .{ self_node, null };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, position, "afterbegin")) {
|
||||||
|
// Get the first child; null indicates there are no children.
|
||||||
|
const first_child = parser.nodeFirstChild(self_node);
|
||||||
|
break :blk .{ self_node, first_child };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, position, "beforebegin")) {
|
||||||
|
// The node must have a parent node in order to use this variant.
|
||||||
|
const parent = parser.nodeParentNode(self_node) orelse return error.NoModificationAllowed;
|
||||||
|
// Parent cannot be Document.
|
||||||
|
// Should have checks for document_fragment and document_type?
|
||||||
|
if (parser.nodeType(parent) == .document) {
|
||||||
|
return error.NoModificationAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
break :blk .{ parent, self_node };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, position, "afterend")) {
|
||||||
|
// The node must have a parent node in order to use this variant.
|
||||||
|
const parent = parser.nodeParentNode(self_node) orelse return error.NoModificationAllowed;
|
||||||
|
// Parent cannot be Document.
|
||||||
|
if (parser.nodeType(parent) == .document) {
|
||||||
|
return error.NoModificationAllowed;
|
||||||
|
}
|
||||||
|
// Get the next sibling or null; null indicates our node is the only one.
|
||||||
|
const sibling = parser.nodeNextSibling(self_node);
|
||||||
|
break :blk .{ parent, sibling };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thrown if:
|
||||||
|
// * position is not one of the four listed values.
|
||||||
|
// * The input is XML that is not well-formed.
|
||||||
|
return error.Syntax;
|
||||||
|
};
|
||||||
|
|
||||||
|
while (parser.nodeListItem(children, 0)) |child| {
|
||||||
|
_ = try parser.nodeInsertBefore(target_node, child, prev_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The closest() method of the Element interface traverses the element and its parents (heading toward the document root) until it finds a node that matches the specified CSS selector.
|
// The closest() method of the Element interface traverses the element and its parents (heading toward the document root) until it finds a node that matches the specified CSS selector.
|
||||||
// Returns the closest ancestor Element or itself, which matches the selectors. If there are no such element, null.
|
// Returns the closest ancestor Element or itself, which matches the selectors. If there are no such element, null.
|
||||||
pub fn _closest(self: *parser.Element, selector: []const u8, page: *Page) !?*parser.Element {
|
pub fn _closest(self: *parser.Element, selector: []const u8, page: *Page) !?*parser.Element {
|
||||||
|
|||||||
@@ -1363,7 +1363,7 @@ pub fn nodeHasChildNodes(node: *Node) bool {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nodeInsertBefore(node: *Node, new_node: *Node, ref_node: *Node) !*Node {
|
pub fn nodeInsertBefore(node: *Node, new_node: *Node, ref_node: ?*Node) !*Node {
|
||||||
var res: ?*Node = null;
|
var res: ?*Node = null;
|
||||||
const err = nodeVtable(node).dom_node_insert_before.?(node, new_node, ref_node, &res);
|
const err = nodeVtable(node).dom_node_insert_before.?(node, new_node, ref_node, &res);
|
||||||
try DOMErr(err);
|
try DOMErr(err);
|
||||||
|
|||||||
@@ -289,3 +289,40 @@
|
|||||||
linkElement.rel = "stylesheet";
|
linkElement.rel = "stylesheet";
|
||||||
testing.expectEqual("stylesheet", linkElement.rel);
|
testing.expectEqual("stylesheet", linkElement.rel);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- This structure will get mutated by insertAdjacentHTML test -->
|
||||||
|
<div id="insert-adjacent-html-outer-wrapper">
|
||||||
|
<div id="insert-adjacent-html-inner-wrapper">
|
||||||
|
<span></span>
|
||||||
|
<p>content</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script id=insertAdjacentHTML>
|
||||||
|
// Insert "beforeend".
|
||||||
|
const wrapper = $("#insert-adjacent-html-inner-wrapper");
|
||||||
|
wrapper.insertAdjacentHTML("beforeend", "<h1>title</h1>");
|
||||||
|
let newElement = wrapper.lastElementChild;
|
||||||
|
testing.expectEqual("H1", newElement.tagName);
|
||||||
|
testing.expectEqual("title", newElement.innerText);
|
||||||
|
|
||||||
|
// Insert "beforebegin".
|
||||||
|
wrapper.insertAdjacentHTML("beforebegin", "<h2>small title</h2>");
|
||||||
|
newElement = wrapper.previousElementSibling;
|
||||||
|
testing.expectEqual("H2", newElement.tagName);
|
||||||
|
testing.expectEqual("small title", newElement.innerText);
|
||||||
|
|
||||||
|
// Insert "afterend".
|
||||||
|
wrapper.insertAdjacentHTML("afterend", "<div id=\"afterend\">after end</div>");
|
||||||
|
newElement = wrapper.nextElementSibling;
|
||||||
|
testing.expectEqual("DIV", newElement.tagName);
|
||||||
|
testing.expectEqual("after end", newElement.innerText);
|
||||||
|
testing.expectEqual("afterend", newElement.id);
|
||||||
|
|
||||||
|
// Insert "afterbegin".
|
||||||
|
wrapper.insertAdjacentHTML("afterbegin", "<div class=\"afterbegin\">after begin</div><yy></yy>");
|
||||||
|
newElement = wrapper.firstElementChild;
|
||||||
|
testing.expectEqual("DIV", newElement.tagName);
|
||||||
|
testing.expectEqual("after begin", newElement.innerText);
|
||||||
|
testing.expectEqual("afterbegin", newElement.className);
|
||||||
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user