@@ -4,6 +4,7 @@ import { caretPositionFromPoint, elementFromPoint } from '../../utils/src/polyfi
44import { targetDeepest } from '../../utils/src/Dom' ;
55import { nodeName , getDocument , nodeLength , isInstanceOf } from '../../utils/src/utils' ;
66import { removeFormattingSpace } from '../../utils/src/formattingSpace' ;
7+ import { isBlock } from '../../utils/src/isBlock' ;
78
89const navigationKey = new Set ( [
910 'ArrowUp' ,
@@ -287,6 +288,7 @@ interface CurrentStackObservations {
287288 keyboardSelectAll ?: CustomEvent ;
288289 } ;
289290 mutations : MutationRecord [ ] ;
291+ flickeringRemoved : Set < EventToProcess > ;
290292}
291293
292294/**
@@ -777,7 +779,10 @@ export class EventNormalizer {
777779 // events occuring during one tick is not enough so we need to delay the
778780 // analysis after we observe events during two ticks instead.
779781 const needSecondTickObservation = currentStackObservation . _events . every ( ev => {
780- return ! MultiStackEventTypes . includes ( ev . type ) ;
782+ return (
783+ ! MultiStackEventTypes . includes ( ev . type ) &&
784+ ! currentStackObservation . flickeringRemoved . has ( ev )
785+ ) ;
781786 } ) ;
782787 if ( needSecondTickObservation && ! secondTickObservation ) {
783788 return await new Promise ( ( resolve ) : void => {
@@ -813,6 +818,7 @@ export class EventNormalizer {
813818 const inferredKeydownEvent : InferredKeydownEvent =
814819 keydownEvent &&
815820 keydownEvent . key === 'Unidentified' &&
821+ inputEvent &&
816822 this . _inferKeydownEvent ( inputEvent ) ;
817823 //
818824 // First pass to get the informations
@@ -876,14 +882,18 @@ export class EventNormalizer {
876882 // multiples keys pushed.
877883 if ( currentStackObservation . _multiKeyStack . length > 1 && possibleMultiKeydown ) {
878884 currentStackObservation . _multiKeyStack . map ( keydownMap => {
879- const keyboardAction = this . _getKeyboardAction (
880- currentStackObservation . mutations ,
881- keydownMap . keydown . key ,
882- ( keydownMap . input && keydownMap . input . inputType ) || '' ,
883- ! ! mutatedElements . size ,
884- ) ;
885- if ( keyboardAction ) {
886- normalizedActions . push ( keyboardAction ) ;
885+ if (
886+ mutatedElements . size ||
887+ currentStackObservation . flickeringRemoved . has ( keydownMap . keydown )
888+ ) {
889+ const keyboardAction = this . _getKeyboardAction (
890+ currentStackObservation . mutations ,
891+ keydownMap . keydown . key ,
892+ ( keydownMap . input && keydownMap . input . inputType ) || '' ,
893+ ) ;
894+ if ( keyboardAction ) {
895+ normalizedActions . push ( keyboardAction ) ;
896+ }
887897 }
888898 } ) ;
889899 } else if ( cutEvent ) {
@@ -906,15 +916,19 @@ export class EventNormalizer {
906916 normalizedActions . length === 0 &&
907917 ( ( ! compositionEvent && key ) || isCompositionKeyboard || isVirtualKeyboard )
908918 ) {
909- const keyboardAction = this . _getKeyboardAction (
910- currentStackObservation . mutations ,
911- key ,
912- inputType ,
913- ! ! mutatedElements . size ,
914- keydownEvent ,
915- ) ;
916- if ( keyboardAction ) {
917- normalizedActions . push ( keyboardAction ) ;
919+ if (
920+ mutatedElements . size ||
921+ currentStackObservation . flickeringRemoved . has ( keydownEvent )
922+ ) {
923+ const keyboardAction = this . _getKeyboardAction (
924+ currentStackObservation . mutations ,
925+ key ,
926+ inputType ,
927+ keydownEvent ,
928+ ) ;
929+ if ( keyboardAction ) {
930+ normalizedActions . push ( keyboardAction ) ;
931+ }
918932 }
919933
920934 if ( compositionReplaceOneChar ) {
@@ -978,6 +992,7 @@ export class EventNormalizer {
978992 _multiKeyStack : [ ] ,
979993 _eventsMap : { } ,
980994 mutations : undefined ,
995+ flickeringRemoved : new Set ( ) ,
981996 } ;
982997 }
983998
@@ -1042,7 +1057,6 @@ export class EventNormalizer {
10421057 mutations : MutationRecord [ ] ,
10431058 key : string ,
10441059 inputType : string ,
1045- hasMutatedElements : boolean ,
10461060 keydownEvent ?: KeyboardEvent ,
10471061 ) :
10481062 | InsertLineBreakAction
@@ -1051,8 +1065,7 @@ export class EventNormalizer {
10511065 | DeleteWordAction
10521066 | DeleteHardLineAction
10531067 | DeleteContentAction {
1054- const isInsertOrRemoveAction = hasMutatedElements && ! inputTypeCommands . has ( inputType ) ;
1055- if ( isInsertOrRemoveAction ) {
1068+ if ( ! inputTypeCommands . has ( inputType ) ) {
10561069 if ( key === 'Backspace' || key === 'Delete' ) {
10571070 return this . _getRemoveAction ( mutations , key , inputType , keydownEvent ) ;
10581071 } else if ( key === 'Enter' ) {
@@ -1720,6 +1733,104 @@ export class EventNormalizer {
17201733 }
17211734 const [ offsetNode , offset ] = targetDeepest ( selection . anchorNode , selection . anchorOffset ) ;
17221735 this . _initialCaretPosition = { offsetNode, offset } ;
1736+
1737+ Promise . resolve ( ) . then ( ( ) => {
1738+ // Wait the next micro task to allow the external features to
1739+ // prevent the default and cancel the normalized keypress.
1740+ if ( ! ev . defaultPrevented && this . _isSafeToPreventKeyboardEvent ( ev , selection ) ) {
1741+ ev . preventDefault ( ) ;
1742+ this . currentStackObservation . flickeringRemoved . add ( ev ) ;
1743+ }
1744+ } ) ;
1745+ }
1746+ /**
1747+ * Check the selection to preventDefault events (Enter, Backspace, Delete)
1748+ * and remove flickering without break the browser auto completion and
1749+ * spellcheckers.
1750+ *
1751+ * @param {KeyboardEvent } ev
1752+ */
1753+ _isSafeToPreventKeyboardEvent ( ev : KeyboardEvent , selection : DomSelectionDescription ) : boolean {
1754+ const code = ev . code || ev . key ;
1755+ if (
1756+ this . _modifierKeys . ctrlKey ||
1757+ this . _modifierKeys . altKey ||
1758+ this . _modifierKeys . metaKey ||
1759+ this . _modifierKeys . shiftKey ||
1760+ ( code !== 'Backspace' && code !== 'Delete' && code !== 'Enter' )
1761+ ) {
1762+ // If a control key is applied, you must let the browser apply its
1763+ // own behavior because you do not know the command in advance.
1764+ return false ;
1765+ }
1766+
1767+ if (
1768+ isBlock ( selection . anchorNode ) ||
1769+ ( selection . anchorNode !== selection . focusNode && isBlock ( selection . focusNode ) )
1770+ ) {
1771+ // If the range is on a block, there is no risk of altering the
1772+ // behavior of the spellcheckers.
1773+ return true ;
1774+ }
1775+
1776+ const previous = this . _geSiblingInlineText (
1777+ selection . anchorNode ,
1778+ selection . anchorOffset ,
1779+ true ,
1780+ ) ;
1781+ const next = this . _geSiblingInlineText ( selection . focusNode , selection . focusOffset , false ) ;
1782+ const regExpChar = / [ ^ \s \t \r \n \u00A0 \u200b ] / ;
1783+
1784+ if (
1785+ code === 'Backspace' &&
1786+ ! regExpChar . test ( previous . slice ( - 2 ) ) &&
1787+ ! regExpChar . test ( next [ 0 ] || '' )
1788+ ) {
1789+ return true ;
1790+ }
1791+ if (
1792+ code === 'Delete' &&
1793+ ! regExpChar . test ( previous [ previous . length - 1 ] || '' ) &&
1794+ ! regExpChar . test ( next . slice ( 0 , 2 ) )
1795+ ) {
1796+ return true ;
1797+ }
1798+ if ( code === 'Enter' && ! regExpChar . test ( previous . slice ( - 2 ) ) ) {
1799+ return true ;
1800+ }
1801+ }
1802+ /**
1803+ * Return the sibling text passing through inlines formatting.
1804+ *
1805+ * @param {KeyboardEvent } ev
1806+ */
1807+ _geSiblingInlineText ( node : Node , offset : number , before = true ) : string {
1808+ let text = '' ;
1809+ while ( node ) {
1810+ if ( node . nodeType === Node . TEXT_NODE ) {
1811+ if ( before ) {
1812+ text += node . textContent . slice ( 0 , offset ) ;
1813+ } else {
1814+ text += node . textContent . slice ( offset ) ;
1815+ }
1816+ } else if ( isBlock ( node ) ) {
1817+ return text ;
1818+ } else {
1819+ text += node . textContent ;
1820+ }
1821+ let sibling = node [ before ? 'previousSibling' : 'nextSibling' ] ;
1822+ while ( node && ! sibling && node . parentNode ) {
1823+ if ( isBlock ( node . parentNode ) ) {
1824+ return text ;
1825+ } else {
1826+ node = node . parentNode ;
1827+ sibling = node [ before ? 'previousSibling' : 'nextSibling' ] ;
1828+ }
1829+ }
1830+ node = sibling ;
1831+ offset = before ? nodeLength ( sibling ) : 0 ;
1832+ }
1833+ return text ;
17231834 }
17241835 /**
17251836 * Set internal properties of the pointer down event to retrieve them later
0 commit comments