From 5a6ee6793336269ec24cdaed9cf4c6eea5946a75 Mon Sep 17 00:00:00 2001 From: farthinker Date: Thu, 8 Aug 2019 19:04:09 +0800 Subject: [PATCH] Fix: IME composistion broken in empty paragraph in Safari. --- src/utils/injecttypingmutationshandling.js | 25 +++++++- tests/input.js | 75 ++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/utils/injecttypingmutationshandling.js b/src/utils/injecttypingmutationshandling.js index 76f4dfa..cf25df0 100644 --- a/src/utils/injecttypingmutationshandling.js +++ b/src/utils/injecttypingmutationshandling.js @@ -242,13 +242,36 @@ class MutationHandler { const modelPos = this.editing.mapper.toModelPosition( viewPos ); const insertedText = change.values[ 0 ].data; + // When we use Chinese IME in Safari, the dom selection won't be collapsed while composing. + // But the input command would still set a collapsed selection after insertion, + // make _domSelectionNeedsUpdate to be true and unnecessarily update the composition selection, + // which would break the IME composistion and causing the bug described in: + // + // https://github.com/ckeditor/ckeditor5/issues/1333 + // + // This fix ensure the input command respects the actual dom selection while composing + // in whichever browser. + let resultRange; + const { document, domConverter } = this.editing.view; + if ( document.isComposing ) { + const domRoot = domConverter.mapViewToDom( document.selection.editableElement ); + const domSelection = domRoot.ownerDocument.getSelection(); + + if ( !domSelection.isCollapsed && domSelection.anchorNode === domSelection.focusNode ) { + resultRange = this.editor.model.createRange( + modelPos, modelPos.getShiftedBy( domSelection.focusOffset - domSelection.anchorOffset ) + ); + } + } + this.editor.execute( 'input', { // Replace   inserted by the browser with normal space. // See comment in `_handleTextMutation`. // In this case we don't need to do this before `diff` because we diff whole nodes. // Just change   in case there are some. text: insertedText.replace( /\u00A0/g, ' ' ), - range: this.editor.model.createRange( modelPos ) + range: this.editor.model.createRange( modelPos ), + resultRange } ); } } diff --git a/tests/input.js b/tests/input.js index 8f862e3..c624c6e 100644 --- a/tests/input.js +++ b/tests/input.js @@ -1017,6 +1017,81 @@ describe( 'Input feature', () => { expect( getModelData( model ) ).to.equal( '<$text bold="true">fo[]<$text italic="true">ar' ); } ); + + it( 'should not break composition in an empty paragraph in Safari', () => { + setModelData( model, '[]' ); + + // When we use Chinese IME in Safari, the dom selection won't be collapsed while composing + const p = viewRoot.getChild( 0 ); + const domP = editor.editing.view.domConverter.mapViewToDom( p ); + const getSelectionStub = testUtils.sinon.stub( document, 'getSelection' ).returns( { + rangeCount: 1, + isCollapsed: false, + anchorNode: domP, + anchorOffset: 0, + focusNode: domP, + focusOffset: 1, + getRangeAt: () => ( { + collapsed: false, + startContainer: domP, + startOffset: 0, + endContainer: domP, + endOffset: 1, + commonAncestorContainer: domP, + } ), + } ); + + viewDocument.fire( 'compositionstart' ); + viewDocument.fire( 'mutations', [ + { + type: 'children', + oldChildren: [], + newChildren: [ new ViewText( 'c' ) ], + node: viewRoot.getChild( 0 ) + } + ] ); + + expect( getSelectionStub.called ).to.equal( true ); + expect( getModelData( model ) ).to.equal( '[c]' ); + getSelectionStub.restore(); + } ); + + it( 'should not break composition in an empty paragraph in browsers except Safari', () => { + setModelData( model, '[]' ); + + const p = viewRoot.getChild( 0 ); + const domP = editor.editing.view.domConverter.mapViewToDom( p ); + const getSelectionStub = testUtils.sinon.stub( document, 'getSelection' ).returns( { + rangeCount: 1, + isCollapsed: true, + anchorNode: domP, + anchorOffset: 1, + focusNode: domP, + focusOffset: 1, + getRangeAt: () => ( { + collapsed: true, + startContainer: domP, + startOffset: 1, + endContainer: domP, + endOffset: 1, + commonAncestorContainer: domP, + } ), + } ); + + viewDocument.fire( 'compositionstart' ); + viewDocument.fire( 'mutations', [ + { + type: 'children', + oldChildren: [], + newChildren: [ new ViewText( 'c' ) ], + node: viewRoot.getChild( 0 ) + } + ] ); + + expect( getSelectionStub.called ).to.equal( true ); + expect( getModelData( model ) ).to.equal( 'c[]' ); + getSelectionStub.restore(); + } ); } ); } ); } );