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:
Josh Matthews 2024-12-26 01:06:09 -05:00 committed by GitHub
parent 20d67bdc44
commit 981616f918
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 117 additions and 51 deletions

View file

@ -55,7 +55,7 @@ use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{
document_from_node, window_from_node, BindContext, ChildrenMutation, CloneChildrenFlag, Node,
document_from_node, window_from_node, ChildrenMutation, CloneChildrenFlag, Node,
};
use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::virtualmethods::VirtualMethods;
@ -1188,25 +1188,32 @@ impl VirtualMethods for HTMLScriptElement {
}
}
/// <https://html.spec.whatwg.org/multipage/#script-processing-model:the-script-element-26>
fn children_changed(&self, mutation: &ChildrenMutation) {
if let Some(s) = self.super_type() {
s.children_changed(mutation);
}
if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
self.prepare(CanGc::note());
if self.upcast::<Node>().is_connected() && !self.parser_inserted.get() {
let script = Trusted::new(self);
// This method can be invoked while there are script/layout blockers present
// as DOM mutations have not yet settled. We use a delayed task to avoid
// running any scripts until the DOM tree is safe for interactions.
document_from_node(self).add_delayed_task(task!(ScriptPrepare: move || {
let this = script.root();
this.prepare(CanGc::note());
}));
}
}
fn bind_to_tree(&self, context: &BindContext) {
/// <https://html.spec.whatwg.org/multipage/#script-processing-model:the-script-element-20>
fn post_connection_steps(&self) {
if let Some(s) = self.super_type() {
s.bind_to_tree(context);
s.post_connection_steps();
}
if context.tree_connected && !self.parser_inserted.get() {
let script = Trusted::new(self);
document_from_node(self).add_delayed_task(task!(ScriptDelayedInitialize: move || {
script.root().prepare(CanGc::note());
}));
if self.upcast::<Node>().is_connected() && !self.parser_inserted.get() {
self.prepare(CanGc::note());
}
}