mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Don't run scripts while DOM tree is undergoing mutations (#34505)
* script: Implement node insertion post-connection hook. Ensure script elements only run scripts when the DOM has stabilized. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * script: Make iframe element use post-connection steps when handling initial document insertion. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * script: Use a delayed task when running post-connection steps. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * script: Add explanatory comment. Signed-off-by: Josh Matthews <josh@joshmatthews.net> * Tidy. Signed-off-by: Josh Matthews <josh@joshmatthews.net> --------- Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
parent
20d67bdc44
commit
981616f918
10 changed files with 117 additions and 51 deletions
|
@ -68,6 +68,7 @@ use crate::dom::bindings::inheritance::{
|
|||
Castable, CharacterDataTypeId, ElementTypeId, EventTargetTypeId, HTMLElementTypeId, NodeTypeId,
|
||||
SVGElementTypeId, SVGGraphicsElementTypeId, TextTypeId,
|
||||
};
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomObject, DomObjectWrap};
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, DomSlice, LayoutDom, MutNullableDom};
|
||||
use crate::dom::bindings::str::{DOMString, USVString};
|
||||
|
@ -2128,6 +2129,38 @@ impl Node {
|
|||
};
|
||||
MutationObserver::queue_a_mutation_record(parent, mutation);
|
||||
}
|
||||
|
||||
// Step 10. Let staticNodeList be a list of nodes, initially « ».
|
||||
let mut static_node_list = vec![];
|
||||
|
||||
// Step 11. For each node of nodes, in tree order:
|
||||
for node in new_nodes {
|
||||
// Step 11.1 For each shadow-including inclusive descendant inclusiveDescendant of node,
|
||||
// in shadow-including tree order, append inclusiveDescendant to staticNodeList.
|
||||
static_node_list.extend(
|
||||
node.traverse_preorder(ShadowIncluding::Yes)
|
||||
.map(|n| Trusted::new(&*n)),
|
||||
);
|
||||
}
|
||||
|
||||
// We use a delayed task for this step to work around an awkward interaction between
|
||||
// script/layout blockers, Node::replace_all, and the children_changed vtable method.
|
||||
// Any node with a post connection step that triggers layout (such as iframes) needs
|
||||
// to be marked as dirty before doing so. This is handled by Node's children_changed
|
||||
// callback, but when Node::insert is called as part of Node::replace_all then the
|
||||
// callback is suppressed until we return to Node::replace_all. To ensure the sequence:
|
||||
// 1) children_changed in Node::replace_all,
|
||||
// 2) post_connection_steps from Node::insert,
|
||||
// we use a delayed task that will run as soon as Node::insert removes its
|
||||
// script/layout blocker.
|
||||
node.owner_doc().add_delayed_task(task!(PostConnectionSteps: move || {
|
||||
// Step 12. For each node of staticNodeList, if node is connected, then run the
|
||||
// post-connection steps with node.
|
||||
for node in static_node_list.iter().map(Trusted::root).filter(|n| n.is_connected()) {
|
||||
vtable_for(&node).post_connection_steps();
|
||||
}
|
||||
}));
|
||||
|
||||
node.owner_doc().remove_script_and_layout_blocker();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue