Skip to content
This repository has been archived by the owner on Feb 6, 2023. It is now read-only.

Normalize input data application from onBeforeInput and compositionUpdate #1419

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"lint": "eslint .",
"format": "eslint . --fix",
"flow": "flow src",
"test": "NODE_ENV=test jest",
"test-ci": "NODE_ENV=test npm run lint && npm run flow && npm run test"
"test": "cross-env NODE_ENV=test jest",
"test-ci": "cross-env NODE_ENV=test npm run lint && npm run flow && npm run test"
},
"dependencies": {
"fbjs": "^0.8.15",
Expand All @@ -45,6 +45,7 @@
"babel-core": "^6.8.0",
"babel-eslint": "^7.2.3",
"babel-preset-fbjs": "^2.1.0",
"cross-env": "5.0.4",
"del": "^2.2.0",
"envify": "^3.4.0",
"es6-shim": "^0.34.4",
Expand Down
3 changes: 3 additions & 0 deletions src/component/base/DraftEditor.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class DraftEditor extends React.Component<DraftEditorProps, State> {
_onCharacterData: Function;
_onCompositionEnd: Function;
_onCompositionStart: Function;
_onCompositionUpdate: Function;
_onCopy: Function;
_onCut: Function;
_onDragEnd: Function;
Expand Down Expand Up @@ -143,6 +144,7 @@ class DraftEditor extends React.Component<DraftEditorProps, State> {
this._onCharacterData = this._buildHandler('onCharacterData');
this._onCompositionEnd = this._buildHandler('onCompositionEnd');
this._onCompositionStart = this._buildHandler('onCompositionStart');
this._onCompositionUpdate = this._buildHandler('onCompositionUpdate');
this._onCopy = this._buildHandler('onCopy');
this._onCut = this._buildHandler('onCut');
this._onDragEnd = this._buildHandler('onDragEnd');
Expand Down Expand Up @@ -287,6 +289,7 @@ class DraftEditor extends React.Component<DraftEditorProps, State> {
onBlur={this._onBlur}
onCompositionEnd={this._onCompositionEnd}
onCompositionStart={this._onCompositionStart}
onCompositionUpdate={this._onCompositionUpdate}
onCopy={this._onCopy}
onCut={this._onCut}
onDragEnd={this._onDragEnd}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,28 @@ const RESOLVE_DELAY = 20;
let resolved = false;
let stillComposing = false;
let textInputData = '';
let beforeInputData = null;
let compositionUpdateData = null;
let compositionEndData = null;

var DraftEditorCompositionHandler = {
/**
* Some IMEs (Firefox Mobile, notably) fire multiple `beforeinput` events
* which include the text so far typed, in addition to (broken)
* `compositionend` events. In these cases, we construct beforeInputData from
* the `beforeinput` and cnsider that to be the definitive version of what
* was actually typed.
*
* Proper, compliant browsers will not do this and will instead include the
* entire resolved composition result in the `data` member of the
* `compositionend` events that they fire.
*/
onBeforeInput: function(editor: DraftEditor, e: SyntheticInputEvent<>): void {
textInputData = (textInputData || '') + e.data;
if (DraftFeatureFlags.draft_enable_composition_fixes) {
beforeInputData = (beforeInputData || '') + e.data;
} else {
textInputData = (textInputData || '') + e.data;
}
},

/**
Expand All @@ -59,6 +77,19 @@ var DraftEditorCompositionHandler = {
stillComposing = true;
},

/**
* A `compositionupdate` event has fired. Update the current composition
* session.
*/
onCompositionUpdate: function(
editor: DraftEditor,
e: SyntheticInputEvent<>,
): void {
if (DraftFeatureFlags.draft_enable_composition_fixes) {
compositionUpdateData = e.data;
}
},

/**
* Attempt to end the current composition session.
*
Expand All @@ -73,9 +104,16 @@ var DraftEditorCompositionHandler = {
* twice could break the DOM, we only use the first event. Example: Arabic
* Google Input Tools on Windows 8.1 fires `compositionend` three times.
*/
onCompositionEnd: function(editor: DraftEditor): void {
onCompositionEnd: function(
editor: DraftEditor,
e: SyntheticCompositionEvent<>,
): void {
resolved = false;
stillComposing = false;
if (DraftFeatureFlags.draft_enable_composition_fixes) {
// Use e.data from the first compositionend event seen
compositionEndData = compositionEndData || e.data;
}
setTimeout(() => {
if (!resolved) {
DraftEditorCompositionHandler.resolveComposition(editor);
Expand Down Expand Up @@ -115,6 +153,34 @@ var DraftEditorCompositionHandler = {
}
},

/**
* Normalizes platform inconsistencies with input event data.
*
* When beforeInputData is present, it is only preferred if its length
* is greater than that of the last compositionUpdate event data. This is
* meant to resolve IME incosistencies where compositionUpdate may contain
* only the last character or the entire composition depending on language
* (e.g. Korean vs. Japanese).
*
* When beforeInputData is not present, compositionUpdate data is preferred.
* This resolves issues with some platforms where beforeInput is never fired
* (e.g. Android with certain keyboard and browser combinations).
*
* Lastly, if neither beforeInput nor compositionUpdate events are fired, use
* the data in the compositionEnd event
*/
normalizeCompositionInput: function(): ?string {
const beforeInputDataLength = beforeInputData ? beforeInputData.length : 0;
const compositionUpdateDataLength = compositionUpdateData
? compositionUpdateData.length
: 0;
const updateData =
beforeInputDataLength > compositionUpdateDataLength
? beforeInputData
: compositionUpdateData;
return updateData || compositionEndData;
},

/**
* Attempt to insert composed characters into the document.
*
Expand All @@ -136,8 +202,17 @@ var DraftEditorCompositionHandler = {
}

resolved = true;
const composedChars = textInputData;
textInputData = '';

let composedChars;
if (DraftFeatureFlags.draft_enable_composition_fixes) {
composedChars = this.normalizeCompositionInput();
beforeInputData = null;
compositionUpdateData = null;
compositionEndData = null;
} else {
composedChars = textInputData;
textInputData = '';
}

const editorState = EditorState.set(editor._latestEditorState, {
inCompositionMode: false,
Expand Down
Loading