diff --git a/packages/ckeditor5-engine/src/view/observer/inputobserver.ts b/packages/ckeditor5-engine/src/view/observer/inputobserver.ts index 73b413f6064..264798aafe2 100644 --- a/packages/ckeditor5-engine/src/view/observer/inputobserver.ts +++ b/packages/ckeditor5-engine/src/view/observer/inputobserver.ts @@ -35,6 +35,13 @@ export default class InputObserver extends DomEventObserver<'beforeinput'> { // @if CK_DEBUG_TYPING // ); // @if CK_DEBUG_TYPING // } + // Handles reverse typing cases where the current document is not seen as the + // DOM `activeElement`, but the editable is somehow still in focus. + // In other words, the editable visually appears to be in focus, but it is not. + if ( !this.view.document.isFocused ) { + // The current target is assumed to be the editable + domEvent.currentTarget?.dispatchEvent( new FocusEvent( 'focus' ) ); + } const domTargetRanges = domEvent.getTargetRanges(); const view = this.view; const viewDocument = view.document; diff --git a/packages/ckeditor5-engine/tests/view/observer/inputobserver.js b/packages/ckeditor5-engine/tests/view/observer/inputobserver.js index 73b47ba5b32..b4755f921aa 100644 --- a/packages/ckeditor5-engine/tests/view/observer/inputobserver.js +++ b/packages/ckeditor5-engine/tests/view/observer/inputobserver.js @@ -59,6 +59,34 @@ describe( 'InputObserver', () => { sinon.assert.calledOnce( beforeInputSpy ); } ); + it( 'should not dispatch focus event if the document has focus', () => { + viewDocument.isFocused = true; + + const mockDispatchEvent = sinon.spy(); + fireMockNativeBeforeInput( { + inputType: 'foo', + currentTarget: { + dispatchEvent: mockDispatchEvent + } + } ); + + expect( mockDispatchEvent.callCount ).to.equal( 0 ); + } ); + + it( 'should dispatch focus event if the document does not have focus', () => { + viewDocument.isFocused = false; + + const mockDispatchEvent = sinon.spy(); + fireMockNativeBeforeInput( { + inputType: 'foo', + currentTarget: { + dispatchEvent: mockDispatchEvent + } + } ); + + expect( mockDispatchEvent.callCount ).to.equal( 1 ); + } ); + describe( 'event data', () => { it( 'should contain #eventType', () => { fireMockNativeBeforeInput( {