diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc index 4dece285b7ab3..80973ac22a435 100644 --- a/third_party/blink/renderer/core/dom/node.cc +++ b/third_party/blink/renderer/core/dom/node.cc @@ -57,6 +57,7 @@ #include "third_party/blink/renderer/core/dom/events/event_dispatcher.h" #include "third_party/blink/renderer/core/dom/events/event_listener.h" #include "third_party/blink/renderer/core/dom/events/event_path.h" +#include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h" #include "third_party/blink/renderer/core/dom/flat_tree_node_data.h" #include "third_party/blink/renderer/core/dom/flat_tree_traversal.h" #include "third_party/blink/renderer/core/dom/focus_params.h" @@ -773,6 +774,14 @@ void Node::moveBefore(Node* new_child, DCHECK(!GetDocument().StatePreservingAtomicMoveInProgress()); GetDocument().SetStatePreservingAtomicMoveInProgress(true); + // Some `ChildrenChanged()` overrides reachable from `insertBefore()` (e.g. + // `HTMLFieldSetElement`) can end up calling `DispatchScopedEvent()`. Without + // an active `EventQueueScope` those events would dispatch synchronously while + // `StatePreservingAtomicMoveInProgress()` is true, allowing author script to + // run while frame-detachment and layout-detachment are suppressed. Defer all + // scoped events until after the atomic-move flag has been cleared. + EventQueueScope defer_scoped_events_until_move_completes; + ContainerNode* old_parent = new_child->parentNode(); insertBefore(new_child, ref_child, exception_state);