diff --git a/lib/codemirror.js b/lib/codemirror.js
index ea358a0f027d917ea7bf5491a8168a51c8a086e0..07ce3ffa19865f502ddfe5c0d8d635211f1e183f 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -1506,10 +1506,11 @@
         input.composing.value = e.data;
       });
       on(div, "compositionend", function(e) {
+        if (!input.composing) return;
         var data = e.data || input.composing.value, sel = input.composing.sel;
         setTimeout(function() {
           operation(cm, applyTextInput)(cm, data, 0, sel);
-          input.composing = false;
+          input.composing = null;
         }, 50);
       });
 
@@ -1614,9 +1615,6 @@
 
     supportsTouch: function() { return true; },
 
-    reset: nothing,
-    resetPosition: nothing,
-
     receivedFocus: function() {
       var input = this;
       if (this.selectionInEditor())
@@ -1691,8 +1689,18 @@
       return true;
     },
 
-    ensurePolled: nothing,
-    onContextMenu: nothing,
+    ensurePolled: function() {
+      if (this.composing) this.forceCompositionEnd();
+    },
+    reset: function() {
+      if (this.composing) this.forceCompositionEnd();
+    },
+    forceCompositionEnd: function() {
+      operation(this.cm, applyTextInput)(this.cm, this.composing.value, 0, this.composing.sel);
+      this.composing = null;
+      this.div.blur();
+      this.div.focus();
+    },
 
     setUneditable: function(node) {
       node.setAttribute("contenteditable", "false");
@@ -1701,7 +1709,10 @@
     onKeyPress: function(e) {
       e.preventDefault();
       operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0);
-    }
+    },
+
+    onContextMenu: nothing,
+    resetPosition: nothing
   }, ContentEditableInput.prototype);
 
   function posToDOM(cm, pos) {