mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-15 15:58:57 +00:00
Handle infinitely recursive mutation observer
FireFox hangs in these cases, but we'd rather handle it gracefully.
This commit is contained in:
@@ -100,6 +100,7 @@ _script_manager: ScriptManager,
|
|||||||
// List of active MutationObservers
|
// List of active MutationObservers
|
||||||
_mutation_observers: std.ArrayList(*MutationObserver) = .{},
|
_mutation_observers: std.ArrayList(*MutationObserver) = .{},
|
||||||
_mutation_delivery_scheduled: bool = false,
|
_mutation_delivery_scheduled: bool = false,
|
||||||
|
_mutation_delivery_depth: u32 = 0,
|
||||||
|
|
||||||
// List of active IntersectionObservers
|
// List of active IntersectionObservers
|
||||||
_intersection_observers: std.ArrayList(*IntersectionObserver) = .{},
|
_intersection_observers: std.ArrayList(*IntersectionObserver) = .{},
|
||||||
@@ -244,6 +245,7 @@ fn reset(self: *Page, comptime initializing: bool) !void {
|
|||||||
|
|
||||||
self._mutation_observers = .{};
|
self._mutation_observers = .{};
|
||||||
self._mutation_delivery_scheduled = false;
|
self._mutation_delivery_scheduled = false;
|
||||||
|
self._mutation_delivery_depth = 0;
|
||||||
self._intersection_observers = .{};
|
self._intersection_observers = .{};
|
||||||
self._intersection_delivery_scheduled = false;
|
self._intersection_delivery_scheduled = false;
|
||||||
self._customized_builtin_definitions = .{};
|
self._customized_builtin_definitions = .{};
|
||||||
@@ -849,6 +851,18 @@ pub fn deliverMutations(self: *Page) void {
|
|||||||
}
|
}
|
||||||
self._mutation_delivery_scheduled = false;
|
self._mutation_delivery_scheduled = false;
|
||||||
|
|
||||||
|
self._mutation_delivery_depth += 1;
|
||||||
|
defer if (!self._mutation_delivery_scheduled) {
|
||||||
|
// reset the depth once nothing is left to be scheduled
|
||||||
|
self._mutation_delivery_depth = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (self._mutation_delivery_depth > 100) {
|
||||||
|
log.err(.page, "page.MutationLimit", .{});
|
||||||
|
self._mutation_delivery_depth = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate backwards to handle observers that disconnect during their callback
|
// Iterate backwards to handle observers that disconnect during their callback
|
||||||
var i = self._mutation_observers.items.len;
|
var i = self._mutation_observers.items.len;
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<div></div>
|
|
||||||
<div id=d1><p id=p1> And</p></div>
|
|
||||||
<div id=d2><p id=p2> And</p></div>
|
|
||||||
<div id=d3><p id=p3> And</p></div>
|
|
||||||
|
|
||||||
<script src="../testing.js"></script>
|
|
||||||
<script id=mutationObserver>
|
|
||||||
// doesn't crash, yay.
|
|
||||||
new MutationObserver(() => {}).observe(document, { childList: true });
|
|
||||||
|
|
||||||
var nb = 0;
|
|
||||||
var mrs;
|
|
||||||
let cb_this1;
|
|
||||||
let mu1 = new MutationObserver(function(mu) {
|
|
||||||
mrs = mu;
|
|
||||||
nb++;
|
|
||||||
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'));
|
|
||||||
testing.expectEqual('foo', mrs[0].attributeName);
|
|
||||||
testing.expectEqual(null, mrs[0].oldValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
var nb2 = 0;
|
|
||||||
var mrs2;
|
|
||||||
var node1 = $('#p1').firstChild;
|
|
||||||
let mu2 = new MutationObserver((mu) => {
|
|
||||||
mrs2 = mu;
|
|
||||||
nb2++;
|
|
||||||
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);
|
|
||||||
testing.expectEqual(' And', mrs2[0].oldValue);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script id=callback>
|
|
||||||
// tests that mutation observers that have a callback which trigger the
|
|
||||||
// mutation observer don't crash.
|
|
||||||
// https://github.com/lightpanda-io/browser/issues/550
|
|
||||||
var node2 = $("#p2");
|
|
||||||
new MutationObserver(() => {
|
|
||||||
node2.innerText = 'a';
|
|
||||||
}).observe($('#d2'), { subtree:true, childList:true });
|
|
||||||
node2.innerText = "2";
|
|
||||||
testing.eventually(() => testing.expectEqual('a', node2.innerText));
|
|
||||||
|
|
||||||
var node3 = $("#p3");
|
|
||||||
var attrWatch = 0;
|
|
||||||
new MutationObserver(() => {
|
|
||||||
attrWatch++;
|
|
||||||
}).observe($('#d3'), { attributeFilter: ["name"], subtree: true });
|
|
||||||
node3.setAttribute("id", "1");
|
|
||||||
|
|
||||||
testing.expectEqual(0, attrWatch);
|
|
||||||
node3.setAttribute('name', 'other');
|
|
||||||
testing.eventually(() => testing.expectEqual(1, attrWatch));
|
|
||||||
</script>
|
|
||||||
Reference in New Issue
Block a user