diff options
author | Josh Matthews <josh@joshmatthews.net> | 2024-12-26 01:06:09 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-26 06:06:09 +0000 |
commit | 981616f91868de481f55e793a7632e6e133a743e (patch) | |
tree | 59d1f960e7d5e5d407dd80613cc6dc471271c99f /components/script/dom/node.rs | |
parent | 20d67bdc441eeb61c52c38dd034eaec6504b3b32 (diff) | |
download | servo-981616f91868de481f55e793a7632e6e133a743e.tar.gz servo-981616f91868de481f55e793a7632e6e133a743e.zip |
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>
Diffstat (limited to 'components/script/dom/node.rs')
-rw-r--r-- | components/script/dom/node.rs | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 496468d2f8c..792812acfbc 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -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(); } |