diff --git a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc index c7eebf2e73161..47b579991d127 100644 --- a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc +++ b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc @@ -34,6 +34,7 @@ #include "third_party/blink/renderer/core/accessibility/scoped_blink_ax_event_intent.h" #include "third_party/blink/renderer/core/css_value_keywords.h" #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h" +#include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h" #include "third_party/blink/renderer/core/dom/focus_params.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/editing/frame_selection.h" @@ -572,8 +573,21 @@ void TextFieldInputType::ValueAttributeChanged() { } void TextFieldInputType::DisabledOrReadonlyAttributeChanged() { - if (SpinButtonElement* spin_button = GetSpinButtonElement()) - spin_button->ReleaseCapture(); + // The disabled state can change as a side-effect of a DOM mutation (e.g. + // reordering children of a disabled
via moveBefore()). + // Queue any resulting change event so it isn't dispatched while we're in the + // middle of a tree mutation, and explicitly suppress dispatch entirely if a + // state-preserving atomic move is in progress, since synchronous script must + // not run while StatePreservingAtomicMoveInProgress is set (it would let the + // page bypass ChildFrameDisconnector for unrelated iframes). + EventQueueScope scope; + if (SpinButtonElement* spin_button = GetSpinButtonElement()) { + SpinButtonElement::EventDispatch dispatch = + GetElement().GetDocument().StatePreservingAtomicMoveInProgress() + ? SpinButtonElement::kEventDispatchDisallowed + : SpinButtonElement::kEventDispatchAllowed; + spin_button->ReleaseCapture(dispatch); + } UpdateWheelEventRegistration(/*is_detaching=*/false); }