prefer DoublyLinkedList for storing MutationObservers in Page

This commit is contained in:
Halil Durak
2025-12-29 16:18:09 +03:00
parent 76ec3eb738
commit ee432c54b8
2 changed files with 24 additions and 22 deletions

View File

@@ -98,7 +98,7 @@ _element_assigned_slots: Element.AssignedSlotLookup = .{},
_script_manager: ScriptManager,
// List of active MutationObservers
_mutation_observers: std.ArrayList(*MutationObserver) = .{},
_mutation_observers: std.DoublyLinkedList = .{},
_mutation_delivery_scheduled: bool = false,
_mutation_delivery_depth: u32 = 0,
@@ -1025,17 +1025,12 @@ pub fn getElementByIdFromNode(self: *Page, node: *Node, id: []const u8) ?*Elemen
return null;
}
pub fn registerMutationObserver(self: *Page, observer: *MutationObserver) !void {
try self._mutation_observers.append(self.arena, observer);
pub inline fn registerMutationObserver(self: *Page, observer: *MutationObserver) !void {
self._mutation_observers.append(&observer.node);
}
pub fn unregisterMutationObserver(self: *Page, observer: *MutationObserver) void {
for (self._mutation_observers.items, 0..) |obs, i| {
if (obs == observer) {
_ = self._mutation_observers.swapRemove(i);
return;
}
}
pub inline fn unregisterMutationObserver(self: *Page, observer: *MutationObserver) void {
self._mutation_observers.remove(&observer.node);
}
pub fn registerIntersectionObserver(self: *Page, observer: *IntersectionObserver) !void {
@@ -1126,11 +1121,9 @@ pub fn deliverMutations(self: *Page) void {
return;
}
// Iterate backwards to handle observers that disconnect during their callback
var i = self._mutation_observers.items.len;
while (i > 0) {
i -= 1;
const observer = self._mutation_observers.items[i];
var it: ?*std.DoublyLinkedList.Node = self._mutation_observers.first;
while (it) |node| : (it = node.next) {
const observer: *MutationObserver = @fieldParentPtr("node", node);
observer.deliverRecords(self) catch |err| {
log.err(.page, "page.deliverMutations", .{ .err = err });
};
@@ -2048,7 +2041,9 @@ pub fn attributeChange(self: *Page, element: *Element, name: []const u8, value:
Element.Html.Custom.invokeAttributeChangedCallbackOnElement(element, name, old_value, value, self);
for (self._mutation_observers.items) |observer| {
var it: ?*std.DoublyLinkedList.Node = self._mutation_observers.first;
while (it) |node| : (it = node.next) {
const observer: *MutationObserver = @fieldParentPtr("node", node);
observer.notifyAttributeChange(element, name, old_value, self) catch |err| {
log.err(.page, "attributeChange.notifyObserver", .{ .err = err });
};
@@ -2072,7 +2067,9 @@ pub fn attributeRemove(self: *Page, element: *Element, name: []const u8, old_val
Element.Html.Custom.invokeAttributeChangedCallbackOnElement(element, name, old_value, null, self);
for (self._mutation_observers.items) |observer| {
var it: ?*std.DoublyLinkedList.Node = self._mutation_observers.first;
while (it) |node| : (it = node.next) {
const observer: *MutationObserver = @fieldParentPtr("node", node);
observer.notifyAttributeChange(element, name, old_value, self) catch |err| {
log.err(.page, "attributeRemove.notifyObserver", .{ .err = err });
};
@@ -2162,7 +2159,7 @@ fn findMatchingSlot(node: *Node, slot_name: []const u8) ?*Element.Html.Slot {
}
pub fn hasMutationObservers(self: *const Page) bool {
return self._mutation_observers.items.len > 0;
return self._mutation_observers.first != null;
}
pub fn getCustomizedBuiltInDefinition(self: *Page, element: *Element) ?*CustomElementDefinition {
@@ -2178,8 +2175,9 @@ pub fn characterDataChange(
target: *Node,
old_value: []const u8,
) void {
// Notify mutation observers
for (self._mutation_observers.items) |observer| {
var it: ?*std.DoublyLinkedList.Node = self._mutation_observers.first;
while (it) |node| : (it = node.next) {
const observer: *MutationObserver = @fieldParentPtr("node", node);
observer.notifyCharacterDataChange(target, old_value, self) catch |err| {
log.err(.page, "cdataChange.notifyObserver", .{ .err = err });
};
@@ -2204,8 +2202,9 @@ pub fn childListChange(
}
}
// Notify mutation observers
for (self._mutation_observers.items) |observer| {
var it: ?*std.DoublyLinkedList.Node = self._mutation_observers.first;
while (it) |node| : (it = node.next) {
const observer: *MutationObserver = @fieldParentPtr("node", node);
observer.notifyChildListChange(target, added_nodes, removed_nodes, previous_sibling, next_sibling, self) catch |err| {
log.err(.page, "childListChange.notifyObserver", .{ .err = err });
};

View File

@@ -34,6 +34,8 @@ const MutationObserver = @This();
_callback: js.Function,
_observing: std.ArrayList(Observing) = .{},
_pending_records: std.ArrayList(*MutationRecord) = .{},
/// Intrusively linked to next element (see Page.zig).
node: std.DoublyLinkedList.Node = .{},
const Observing = struct {
target: *Node,
@@ -53,6 +55,7 @@ pub const ObserveOptions = struct {
pub fn init(callback: js.Function, page: *Page) !*MutationObserver {
return page._factory.create(MutationObserver{
._callback = callback,
.node = .{},
});
}