Problem/Motivation
In Safari and Firefox, when a CKEditor 5 balloon panel (e.g. the link dialog) is open and the user clicks outside the inline editor, the toolbar remains visible as collapsed three-dots in .ck-body-wrapper. Clicking this artifact opens the toolbar in an arbitrary screen position, allowing formatting operations to queue up and apply simultaneously when the editor is refocused.
This is caused by an upstream CKEditor 5 bug (#18978). The link plugin's _removeFormView() unconditionally calls saveButtonView.focus() during balloon cleanup, which re-triggers the FocusTracker's _focus() method and sets isFocused back to true. The inline toolbar panel is bound to isFocused, so it stays visible in the now-hidden container.
In Chrome, document.activeElement moves away from hidden elements synchronously, so the FocusTracker's deferred blur eventually fires. In Safari and Firefox, document.activeElement doesn't move eagerly, so the blur guard in FocusTracker._blur() suppresses the blur entirely and isFocused stays true permanently.
Steps to reproduce
- Open a WYSIWYG editor field using the Change tool (Safari or Firefox)
- Create or select a link to open the link balloon
- Click outside the editor field without closing the link balloon
- Observe the three-dots artifact on screen
- Click the artifact — the toolbar appears in an arbitrary location
- Click formatting buttons, then click back into the editor — all queued changes apply at once
Proposed resolution
Add a defocusCkeditor() method to the InlineEditor field plugin that temporarily disables FocusTracker._focus() before the container is hidden, preventing CKEditor's balloon cleanup from re-asserting focus. The method:
- No-ops
editor.ui.focusTracker._focus - Sets
isFocused = falseandfocusedElement = null - Restores
_focusviasetTimeout(0)after the event cycle completes
Called from blurElement() before replaceFormItemWithPageElement() hides the container.
This is a workaround for the upstream CKEditor 5 bug. The upstream fix is a one-line change: making saveButtonView.focus() in _removeFormView() conditional on the updateFocus parameter it already accepts. This method should be removed when the upstream fix lands in Drupal core's vendored CKEditor 5.
Remaining tasks
- Review and merge the MR
- Monitor ckeditor/ckeditor5#18978 for upstream fix
- Remove
defocusCkeditor()when upstream fix is included in Drupal core's CKEditor 5 vendor
Comments
Comment #3
tim bozeman commented