diff --git a/src/component/handlers/edit/__tests__/editOnBeforeInput-test.js b/src/component/handlers/edit/__tests__/editOnBeforeInput-test.js index 960bd68da6..5180991275 100644 --- a/src/component/handlers/edit/__tests__/editOnBeforeInput-test.js +++ b/src/component/handlers/edit/__tests__/editOnBeforeInput-test.js @@ -218,12 +218,14 @@ test('decorator fingerprint logic bails out of native insertion', () => { testDecoratorFingerprint('hi #', 4, 'f', true); testDecoratorFingerprint('x #foo', 3, '#', true); testDecoratorFingerprint('#foobar', 4, ' ', true); + testDecoratorFingerprint('#foo', 4, 'b', true); + testDecoratorFingerprint('#foo bar #baz', 2, 'o', true); + testDecoratorFingerprint('#foo bar #baz', 12, 'a', true); // but these are OK to let through - testDecoratorFingerprint('#foo', 4, 'b', false); - testDecoratorFingerprint('#foo bar #baz', 2, 'o', false); testDecoratorFingerprint('#foo bar #baz', 7, 'o', false); - testDecoratorFingerprint('#foo bar #baz', 12, 'a', false); + testDecoratorFingerprint('start #foo bar #baz end', 5, 'a', false); + testDecoratorFingerprint('start #foo bar #baz end', 20, 'a', false); } finally { global.getSelection = oldGetSelection; } diff --git a/src/component/handlers/edit/editOnBeforeInput.js b/src/component/handlers/edit/editOnBeforeInput.js index e79b4d5281..f0be9ad894 100644 --- a/src/component/handlers/edit/editOnBeforeInput.js +++ b/src/component/handlers/edit/editOnBeforeInput.js @@ -199,13 +199,15 @@ function editOnBeforeInput( // // 5. '[#foo]' and append 'b' // desired rendering: '[#foob]' - // native rendering would be: '[#foob]' (native insertion is OK here) + // native rendering would be: '[#foob]' + // (native insertion here would be ok for decorators like simple spans, + // but not more complex decorators. To be safe, we need to prevent it.) // // It is safe to allow native insertion if and only if the full list of - // decorator ranges matches what we expect native insertion to give. We - // don't need to compare the content because the only possible mutation - // to consider here is inserting plain text and decorators can't affect - // text content. + // decorator ranges matches what we expect native insertion to give, and + // the range lengths have not changed. We don't need to compare the content + // because the only possible mutation to consider here is inserting plain + // text and decorators can't affect text content. const oldBlockTree = editorState.getBlockTree(anchorKey); const newBlockTree = newEditorState.getBlockTree(anchorKey); mustPreventNative = @@ -218,14 +220,19 @@ function editOnBeforeInput( const oldEnd = oldLeafSet.get('end'); const adjustedEnd = oldEnd + (oldEnd >= selectionStart ? chars.length : 0); + const newStart = newLeafSet.get('start'); + const newEnd = newLeafSet.get('end'); + const newDecoratorKey = newLeafSet.get('decoratorKey'); return ( // Different decorators - oldLeafSet.get('decoratorKey') !== newLeafSet.get('decoratorKey') || + oldLeafSet.get('decoratorKey') !== newDecoratorKey || // Different number of inline styles oldLeafSet.get('leaves').size !== newLeafSet.get('leaves').size || // Different effective decorator position - adjustedStart !== newLeafSet.get('start') || - adjustedEnd !== newLeafSet.get('end') + adjustedStart !== newStart || + adjustedEnd !== newEnd || + // Decorator already existed and its length changed + (newDecoratorKey != null && newEnd - newStart !== oldEnd - oldStart) ); }); }