Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 64 additions & 14 deletions src/browser/dom/mutation_observer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ const Walker = @import("../dom/walker.zig").WalkerChildren;
pub const MutationObserver = struct {
page: *Page,
cbk: Env.Function,
connected: bool,
scheduled: bool,
observers: std.ArrayListUnmanaged(*Observer),

// List of records which were observed. When the call scope ends, we need to
// execute our callback with it.
Expand All @@ -48,8 +48,8 @@ pub const MutationObserver = struct {
.cbk = cbk,
.page = page,
.observed = .{},
.connected = true,
.scheduled = false,
.observers = .empty,
};
}

Expand All @@ -68,39 +68,41 @@ pub const MutationObserver = struct {
.event_node = .{ .id = self.cbk.id, .func = Observer.handle },
};

try self.observers.append(arena, observer);

// register node's events
if (options.childList or options.subtree) {
_ = try parser.eventTargetAddEventListener(
observer.dom_node_inserted_listener = try parser.eventTargetAddEventListener(
parser.toEventTarget(parser.Node, node),
"DOMNodeInserted",
&observer.event_node,
false,
);
_ = try parser.eventTargetAddEventListener(
observer.dom_node_removed_listener = try parser.eventTargetAddEventListener(
parser.toEventTarget(parser.Node, node),
"DOMNodeRemoved",
&observer.event_node,
false,
);
}
if (options.attr()) {
_ = try parser.eventTargetAddEventListener(
observer.dom_node_attribute_modified_listener = try parser.eventTargetAddEventListener(
parser.toEventTarget(parser.Node, node),
"DOMAttrModified",
&observer.event_node,
false,
);
}
if (options.cdata()) {
_ = try parser.eventTargetAddEventListener(
observer.dom_cdata_modified_listener = try parser.eventTargetAddEventListener(
parser.toEventTarget(parser.Node, node),
"DOMCharacterDataModified",
&observer.event_node,
false,
);
}
if (options.subtree) {
_ = try parser.eventTargetAddEventListener(
observer.dom_subtree_modified_listener = try parser.eventTargetAddEventListener(
parser.toEventTarget(parser.Node, node),
"DOMSubtreeModified",
&observer.event_node,
Expand All @@ -111,10 +113,6 @@ pub const MutationObserver = struct {

fn callback(ctx: *anyopaque) ?u32 {
const self: *MutationObserver = @ptrCast(@alignCast(ctx));
if (self.connected == false) {
self.scheduled = true;
return null;
}
self.scheduled = false;

const records = self.observed.items;
Expand All @@ -125,7 +123,7 @@ pub const MutationObserver = struct {
defer self.observed.clearRetainingCapacity();

var result: Env.Function.Result = undefined;
self.cbk.tryCall(void, .{records}, &result) catch {
self.cbk.tryCallWithThis(void, self, .{records}, &result) catch {
log.debug(.user_script, "callback error", .{
.err = result.exception,
.stack = result.stack,
Expand All @@ -135,9 +133,55 @@ pub const MutationObserver = struct {
return null;
}

// TODO
pub fn _disconnect(self: *MutationObserver) !void {
self.connected = false;
for (self.observers.items) |observer| {
const event_target = parser.toEventTarget(parser.Node, observer.node);
if (observer.dom_node_inserted_listener) |listener| {
try parser.eventTargetRemoveEventListener(
event_target,
"DOMNodeInserted",
listener,
false,
);
}

if (observer.dom_node_removed_listener) |listener| {
try parser.eventTargetRemoveEventListener(
event_target,
"DOMNodeRemoved",
listener,
false,
);
}

if (observer.dom_node_attribute_modified_listener) |listener| {
try parser.eventTargetRemoveEventListener(
event_target,
"DOMAttrModified",
listener,
false,
);
}

if (observer.dom_cdata_modified_listener) |listener| {
try parser.eventTargetRemoveEventListener(
event_target,
"DOMCharacterDataModified",
listener,
false,
);
}

if (observer.dom_subtree_modified_listener) |listener| {
try parser.eventTargetRemoveEventListener(
event_target,
"DOMSubtreeModified",
listener,
false,
);
}
}
self.observers.clearRetainingCapacity();
}

// TODO
Expand Down Expand Up @@ -222,6 +266,12 @@ const Observer = struct {

event_node: parser.EventNode,

dom_node_inserted_listener: ?*parser.EventListener = null,
dom_node_removed_listener: ?*parser.EventListener = null,
dom_node_attribute_modified_listener: ?*parser.EventListener = null,
dom_cdata_modified_listener: ?*parser.EventListener = null,
dom_subtree_modified_listener: ?*parser.EventListener = null,

fn appliesTo(
self: *const Observer,
target: *parser.Node,
Expand Down
15 changes: 11 additions & 4 deletions src/tests/dom/mutation_observer.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@

var nb = 0;
var mrs;
new MutationObserver((mu) => {
let cb_this1;
let mu1 = new MutationObserver(function(mu) {
mrs = mu;
nb++;
}).observe(document.firstElementChild, { attributes: true, attributeOldValue: true });
cb_this1 = this;
});
mu1.observe(document.firstElementChild, { attributes: true, attributeOldValue: true });
document.firstElementChild.setAttribute("foo", "bar");
document.firstElementChild.firstChild.setAttribute("foo", "bar");

testing.eventually(() => {
testing.expectEqual(1, nb);
testing.expectEqual(cb_this1, mu1);
testing.expectEqual('attributes', mrs[0].type);
testing.expectEqual(document.firstElementChild, mrs[0].target);
testing.expectEqual('bar', mrs[0].target.getAttribute('foo'));
Expand All @@ -30,14 +34,17 @@
var nb2 = 0;
var mrs2;
var node1 = $('#p1').firstChild;
new MutationObserver((mu) => {
let mu2 = new MutationObserver((mu) => {
mrs2 = mu;
nb2++;
}).observe(node1, { characterData: true, characterDataOldValue: true });
cb_this2 = this;
})
mu2.observe(node1, { characterData: true, characterDataOldValue: true });
node1.data = "foo";

testing.eventually(() => {
testing.expectEqual(1, nb2);
testing.expectEqual(window, cb_this2);
testing.expectEqual('characterData', mrs2[0].type);
testing.expectEqual(node1, mrs2[0].target);
testing.expectEqual('foo', mrs2[0].target.data);
Expand Down