From 82e29adfc6ec6b79c894a180b8af83f21c29471c Mon Sep 17 00:00:00 2001 From: farthinker Date: Wed, 14 Aug 2019 18:55:40 +0800 Subject: [PATCH] Fix: IME composistion broken in empty paragraph in Safari. --- src/view/renderer.js | 12 ++++++++++++ src/view/view.js | 1 + tests/view/renderer.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/src/view/renderer.js b/src/view/renderer.js index c8bc5cc2e..93b0d588d 100644 --- a/src/view/renderer.js +++ b/src/view/renderer.js @@ -101,6 +101,13 @@ export default class Renderer { */ this.isFocused = false; + /** + * Indicates if the view document is composing. + * + * @member {Boolean} + */ + this.isComposing = false; + /** * The text node in which the inline filler was rendered. * @@ -771,6 +778,11 @@ export default class Renderer { * @returns {Boolean} */ _domSelectionNeedsUpdate( domSelection ) { + // Remain DOM selection untouched while composing (#1782) + if ( this.isComposing ) { + return false; + } + if ( !this.domConverter.isDomSelectionCorrect( domSelection ) ) { // Current DOM selection is in incorrect position. We need to update it. return true; diff --git a/src/view/view.js b/src/view/view.js index 6101f5c41..f7f999b34 100644 --- a/src/view/view.js +++ b/src/view/view.js @@ -105,6 +105,7 @@ export default class View { */ this._renderer = new Renderer( this.domConverter, this.document.selection ); this._renderer.bind( 'isFocused' ).to( this.document ); + this._renderer.bind( 'isComposing' ).to( this.document ); /** * A DOM root attributes cache. It saves the initial values of DOM root attributes before the DOM element diff --git a/tests/view/renderer.js b/tests/view/renderer.js index 617a7fda6..78b092e10 100644 --- a/tests/view/renderer.js +++ b/tests/view/renderer.js @@ -3641,6 +3641,48 @@ describe( 'Renderer', () => { return viewData.repeat( repeat ); } } ); + + // #1782 + it( 'should leave dom selection untouched while composing', () => { + const { view: viewP, selection: newSelection } = parse( '[]' ); + + viewRoot._appendChild( viewP ); + selection._setTo( newSelection ); + + renderer.markToSync( 'children', viewRoot ); + renderer.render(); + + // mock IME typing in Safari:

[c]

+ renderer.isComposing = true; + const domText = document.createTextNode( 'c' ); + domRoot.firstChild.appendChild( domText ); + const range = document.createRange(); + range.setStart( domText, 0 ); + range.setEnd( domText, 1 ); + const domSelection = document.getSelection(); + domSelection.removeAllRanges(); + domSelection.addRange( range ); + + // c[] + viewP._appendChild( new ViewText( 'c' ) ); + selection._setTo( [ + new ViewRange( new ViewPosition( viewP.getChild( 0 ), 1 ), new ViewPosition( viewP.getChild( 0 ), 1 ) ) + ] ); + + renderer.markToSync( 'children', viewP ); + renderer.render(); + + expect( domRoot.childNodes.length ).to.equal( 1 ); + expect( domRoot.firstChild.childNodes.length ).to.equal( 1 ); + expect( domRoot.firstChild.firstChild.data ).to.equal( 'c' ); + + const currentRange = domSelection.getRangeAt( 0 ); + expect( currentRange.collapsed ).to.equal( false ); + expect( currentRange.startContainer ).to.equal( domRoot.firstChild.firstChild ); + expect( currentRange.startOffset ).to.equal( 0 ); + expect( currentRange.endContainer ).to.equal( domRoot.firstChild.firstChild ); + expect( currentRange.endOffset ).to.equal( 1 ); + } ); } ); describe( '#922', () => {