From 0ba37372daaf31c93bd2610c07d28fba587bb61d Mon Sep 17 00:00:00 2001 From: Kuba Niegowski Date: Mon, 19 Jun 2023 17:50:39 +0200 Subject: [PATCH] Fixed beforeInput target ranges that start before the editable element. --- .../src/view/observer/inputobserver.ts | 15 ++++++++++-- .../tests/view/observer/inputobserver.js | 24 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/ckeditor5-engine/src/view/observer/inputobserver.ts b/packages/ckeditor5-engine/src/view/observer/inputobserver.ts index 9f1efa2aac3..cc0c39841d9 100644 --- a/packages/ckeditor5-engine/src/view/observer/inputobserver.ts +++ b/packages/ckeditor5-engine/src/view/observer/inputobserver.ts @@ -79,8 +79,19 @@ export default class InputObserver extends DomEventObserver<'beforeinput'> { // @if CK_DEBUG_TYPING // } } else if ( domTargetRanges.length ) { targetRanges = domTargetRanges.map( domRange => { - return view.domConverter.domRangeToView( domRange )!; - } ); + // Sometimes browser provides range that starts before editable node. + // We try to fall back to collapsed range at the valid end position. + // See https://github.com/ckeditor/ckeditor5/issues/14411. + // See https://github.com/ckeditor/ckeditor5/issues/14050. + const viewStart = view.domConverter.domPositionToView( domRange.startContainer, domRange.startOffset ); + const viewEnd = view.domConverter.domPositionToView( domRange.endContainer, domRange.endOffset ); + + if ( viewStart ) { + return view.createRange( viewStart, viewEnd ); + } else if ( viewEnd ) { + return view.createRange( viewEnd ); + } + } ).filter( ( range ): range is ViewRange => !!range ); // @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) { // @if CK_DEBUG_TYPING // console.info( '%c[InputObserver]%c using target ranges:', diff --git a/packages/ckeditor5-engine/tests/view/observer/inputobserver.js b/packages/ckeditor5-engine/tests/view/observer/inputobserver.js index 206440bd8a8..97d0bafcf92 100644 --- a/packages/ckeditor5-engine/tests/view/observer/inputobserver.js +++ b/packages/ckeditor5-engine/tests/view/observer/inputobserver.js @@ -133,6 +133,30 @@ describe( 'InputObserver', () => { expect( viewRange2.end.offset ).to.equal( 2 ); } ); + it( 'should provide fixed editing view ranges corresponding to DOM ranges passed along with the DOM event', () => { + const domRange = global.document.createRange(); + + // [
+ //

fo]o

+ domRange.setStartBefore( domEditable ); + domRange.setEnd( domEditable.firstChild.firstChild, 2 ); + + fireMockNativeBeforeInput( { + getTargetRanges: () => [ domRange ] + } ); + + expect( evtData.targetRanges ).to.have.length( 1 ); + + const viewRange = evtData.targetRanges[ 0 ]; + + expect( viewRange ).to.be.instanceOf( Range ); + + expect( viewRange.start.parent ).to.equal( viewRoot.getChild( 0 ).getChild( 0 ) ); + expect( viewRange.start.offset ).to.equal( 2 ); + expect( viewRange.end.parent ).to.equal( viewRoot.getChild( 0 ).getChild( 0 ) ); + expect( viewRange.end.offset ).to.equal( 2 ); + } ); + it( 'should provide a range encompassing the selected object when selection is fake', () => { const domRange = global.document.createRange();