@@ -45,10 +45,24 @@ const RESOLVE_DELAY = 20;
4545let resolved = false ;
4646let stillComposing = false ;
4747let textInputData = '' ;
48+ let beforeInputData = null ;
49+ let compositionUpdateData = null ;
50+ let compositionEndData = null ;
4851
4952var DraftEditorCompositionHandler = {
53+ /**
54+ * Some IMEs (Firefox Mobile, notably) fire multiple `beforeinput` events
55+ * which include the text so far typed, in addition to (broken)
56+ * `compositionend` events. In these cases, we construct beforeInputData from
57+ * the `beforeinput` and cnsider that to be the definitive version of what
58+ * was actually typed.
59+ *
60+ * Proper, compliant browsers will not do this and will instead include the
61+ * entire resolved composition result in the `data` member of the
62+ * `compositionend` events that they fire.
63+ */
5064 onBeforeInput : function ( editor : DraftEditor , e : SyntheticInputEvent < > ) : void {
51- textInputData = ( textInputData || '' ) + e . data ;
65+ beforeInputData = ( beforeInputData || '' ) + e . data ;
5266 } ,
5367
5468 /**
@@ -59,6 +73,17 @@ var DraftEditorCompositionHandler = {
5973 stillComposing = true ;
6074 } ,
6175
76+ /**
77+ * A `compositionupdate` event has fired. Update the current composition
78+ * session.
79+ */
80+ onCompositionUpdate : function (
81+ editor : DraftEditor ,
82+ e : SyntheticInputEvent < > ,
83+ ) : void {
84+ compositionUpdateData = e . data ;
85+ } ,
86+
6287 /**
6388 * Attempt to end the current composition session.
6489 *
@@ -73,9 +98,14 @@ var DraftEditorCompositionHandler = {
7398 * twice could break the DOM, we only use the first event. Example: Arabic
7499 * Google Input Tools on Windows 8.1 fires `compositionend` three times.
75100 */
76- onCompositionEnd : function ( editor : DraftEditor ) : void {
101+ onCompositionEnd : function (
102+ editor : DraftEditor ,
103+ e : SyntheticCompositionEvent < > ,
104+ ) : void {
77105 resolved = false ;
78106 stillComposing = false ;
107+ // Use e.data from the first compositionend event seen
108+ compositionEndData = compositionEndData || e . data ;
79109 setTimeout ( ( ) => {
80110 if ( ! resolved ) {
81111 DraftEditorCompositionHandler . resolveComposition ( editor ) ;
@@ -115,6 +145,34 @@ var DraftEditorCompositionHandler = {
115145 }
116146 } ,
117147
148+ /**
149+ * Normalizes platform inconsistencies with input event data.
150+ *
151+ * When beforeInputData is present, it is only preferred if its length
152+ * is greater than that of the last compositionUpdate event data. This is
153+ * meant to resolve IME incosistencies where compositionUpdate may contain
154+ * only the last character or the entire composition depending on language
155+ * (e.g. Korean vs. Japanese).
156+ *
157+ * When beforeInputData is not present, compositionUpdate data is preferred.
158+ * This resolves issues with some platforms where beforeInput is never fired
159+ * (e.g. Android with certain keyboard and browser combinations).
160+ *
161+ * Lastly, if neither beforeInput nor compositionUpdate events are fired, use
162+ * the data in the compositionEnd event
163+ */
164+ normalizeCompositionInput : function ( ) : ?string {
165+ const beforeInputDataLength = beforeInputData ? beforeInputData . length : 0 ;
166+ const compositionUpdateDataLength = compositionUpdateData
167+ ? compositionUpdateData . length
168+ : 0 ;
169+ const updateData =
170+ beforeInputDataLength > compositionUpdateDataLength
171+ ? beforeInputData
172+ : compositionUpdateData ;
173+ return updateData || compositionEndData ;
174+ } ,
175+
118176 /**
119177 * Attempt to insert composed characters into the document.
120178 *
@@ -136,8 +194,12 @@ var DraftEditorCompositionHandler = {
136194 }
137195
138196 resolved = true ;
139- const composedChars = textInputData ;
140- textInputData = '' ;
197+
198+ let composedChars ;
199+ composedChars = this . normalizeCompositionInput ( ) ;
200+ beforeInputData = null ;
201+ compositionUpdateData = null ;
202+ compositionEndData = null ;
141203
142204 const editorState = EditorState . set ( editor . _latestEditorState , {
143205 inCompositionMode : false ,
0 commit comments