Skip to content

Commit c335a54

Browse files
authored
Merge pull request #1107 from lightpanda-io/mutation_observer_improvement
Use correct 'this' on MutationObserver callback
2 parents 5bcccec + b47d8a7 commit c335a54

File tree

2 files changed

+75
-18
lines changed

2 files changed

+75
-18
lines changed

src/browser/dom/mutation_observer.zig

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ const Walker = @import("../dom/walker.zig").WalkerChildren;
3636
pub const MutationObserver = struct {
3737
page: *Page,
3838
cbk: Env.Function,
39-
connected: bool,
4039
scheduled: bool,
40+
observers: std.ArrayListUnmanaged(*Observer),
4141

4242
// List of records which were observed. When the call scope ends, we need to
4343
// execute our callback with it.
@@ -48,8 +48,8 @@ pub const MutationObserver = struct {
4848
.cbk = cbk,
4949
.page = page,
5050
.observed = .{},
51-
.connected = true,
5251
.scheduled = false,
52+
.observers = .empty,
5353
};
5454
}
5555

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

71+
try self.observers.append(arena, observer);
72+
7173
// register node's events
7274
if (options.childList or options.subtree) {
73-
_ = try parser.eventTargetAddEventListener(
75+
observer.dom_node_inserted_listener = try parser.eventTargetAddEventListener(
7476
parser.toEventTarget(parser.Node, node),
7577
"DOMNodeInserted",
7678
&observer.event_node,
7779
false,
7880
);
79-
_ = try parser.eventTargetAddEventListener(
81+
observer.dom_node_removed_listener = try parser.eventTargetAddEventListener(
8082
parser.toEventTarget(parser.Node, node),
8183
"DOMNodeRemoved",
8284
&observer.event_node,
8385
false,
8486
);
8587
}
8688
if (options.attr()) {
87-
_ = try parser.eventTargetAddEventListener(
89+
observer.dom_node_attribute_modified_listener = try parser.eventTargetAddEventListener(
8890
parser.toEventTarget(parser.Node, node),
8991
"DOMAttrModified",
9092
&observer.event_node,
9193
false,
9294
);
9395
}
9496
if (options.cdata()) {
95-
_ = try parser.eventTargetAddEventListener(
97+
observer.dom_cdata_modified_listener = try parser.eventTargetAddEventListener(
9698
parser.toEventTarget(parser.Node, node),
9799
"DOMCharacterDataModified",
98100
&observer.event_node,
99101
false,
100102
);
101103
}
102104
if (options.subtree) {
103-
_ = try parser.eventTargetAddEventListener(
105+
observer.dom_subtree_modified_listener = try parser.eventTargetAddEventListener(
104106
parser.toEventTarget(parser.Node, node),
105107
"DOMSubtreeModified",
106108
&observer.event_node,
@@ -111,10 +113,6 @@ pub const MutationObserver = struct {
111113

112114
fn callback(ctx: *anyopaque) ?u32 {
113115
const self: *MutationObserver = @ptrCast(@alignCast(ctx));
114-
if (self.connected == false) {
115-
self.scheduled = true;
116-
return null;
117-
}
118116
self.scheduled = false;
119117

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

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

138-
// TODO
139136
pub fn _disconnect(self: *MutationObserver) !void {
140-
self.connected = false;
137+
for (self.observers.items) |observer| {
138+
const event_target = parser.toEventTarget(parser.Node, observer.node);
139+
if (observer.dom_node_inserted_listener) |listener| {
140+
try parser.eventTargetRemoveEventListener(
141+
event_target,
142+
"DOMNodeInserted",
143+
listener,
144+
false,
145+
);
146+
}
147+
148+
if (observer.dom_node_removed_listener) |listener| {
149+
try parser.eventTargetRemoveEventListener(
150+
event_target,
151+
"DOMNodeRemoved",
152+
listener,
153+
false,
154+
);
155+
}
156+
157+
if (observer.dom_node_attribute_modified_listener) |listener| {
158+
try parser.eventTargetRemoveEventListener(
159+
event_target,
160+
"DOMAttrModified",
161+
listener,
162+
false,
163+
);
164+
}
165+
166+
if (observer.dom_cdata_modified_listener) |listener| {
167+
try parser.eventTargetRemoveEventListener(
168+
event_target,
169+
"DOMCharacterDataModified",
170+
listener,
171+
false,
172+
);
173+
}
174+
175+
if (observer.dom_subtree_modified_listener) |listener| {
176+
try parser.eventTargetRemoveEventListener(
177+
event_target,
178+
"DOMSubtreeModified",
179+
listener,
180+
false,
181+
);
182+
}
183+
}
184+
self.observers.clearRetainingCapacity();
141185
}
142186

143187
// TODO
@@ -222,6 +266,12 @@ const Observer = struct {
222266

223267
event_node: parser.EventNode,
224268

269+
dom_node_inserted_listener: ?*parser.EventListener = null,
270+
dom_node_removed_listener: ?*parser.EventListener = null,
271+
dom_node_attribute_modified_listener: ?*parser.EventListener = null,
272+
dom_cdata_modified_listener: ?*parser.EventListener = null,
273+
dom_subtree_modified_listener: ?*parser.EventListener = null,
274+
225275
fn appliesTo(
226276
self: *const Observer,
227277
target: *parser.Node,

src/tests/dom/mutation_observer.html

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,19 @@
1111

1212
var nb = 0;
1313
var mrs;
14-
new MutationObserver((mu) => {
14+
let cb_this1;
15+
let mu1 = new MutationObserver(function(mu) {
1516
mrs = mu;
1617
nb++;
17-
}).observe(document.firstElementChild, { attributes: true, attributeOldValue: true });
18+
cb_this1 = this;
19+
});
20+
mu1.observe(document.firstElementChild, { attributes: true, attributeOldValue: true });
1821
document.firstElementChild.setAttribute("foo", "bar");
1922
document.firstElementChild.firstChild.setAttribute("foo", "bar");
2023

2124
testing.eventually(() => {
2225
testing.expectEqual(1, nb);
26+
testing.expectEqual(cb_this1, mu1);
2327
testing.expectEqual('attributes', mrs[0].type);
2428
testing.expectEqual(document.firstElementChild, mrs[0].target);
2529
testing.expectEqual('bar', mrs[0].target.getAttribute('foo'));
@@ -30,14 +34,17 @@
3034
var nb2 = 0;
3135
var mrs2;
3236
var node1 = $('#p1').firstChild;
33-
new MutationObserver((mu) => {
37+
let mu2 = new MutationObserver((mu) => {
3438
mrs2 = mu;
3539
nb2++;
36-
}).observe(node1, { characterData: true, characterDataOldValue: true });
40+
cb_this2 = this;
41+
})
42+
mu2.observe(node1, { characterData: true, characterDataOldValue: true });
3743
node1.data = "foo";
3844

3945
testing.eventually(() => {
4046
testing.expectEqual(1, nb2);
47+
testing.expectEqual(window, cb_this2);
4148
testing.expectEqual('characterData', mrs2[0].type);
4249
testing.expectEqual(node1, mrs2[0].target);
4350
testing.expectEqual('foo', mrs2[0].target.data);

0 commit comments

Comments
 (0)