Limit recapitalization for reasonable performance.

At 100k text, it's reasonably fast (less than 1s on latest hardware).

Bug: 12913404
Change-Id: I426b918b2610af24364934a1c37a7314f1142ad0
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index efc5a61..05d3476 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -158,6 +158,10 @@
     // A hint on how many characters to cache from the TextView. A good value of this is given by
     // how many characters we need to be able to almost always find the caps mode.
     public static final int EDITOR_CONTENTS_CACHE_SIZE = 1024;
+    // How many characters we accept for the recapitalization functionality. This needs to be
+    // large enough for all reasonable purposes, but avoid purposeful attacks. 100k sounds about
+    // right for this.
+    public static final int MAX_CHARACTERS_FOR_RECAPITALIZATION = 1024 * 100;
 
     // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
     public static final int DICTIONARY_MAX_WORD_LENGTH = 48;
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 4fac5ed..d4833f5 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -1139,15 +1139,21 @@
         if (!mConnection.hasSelection()) {
             return; // No selection
         }
+        final int selectionStart = mConnection.getExpectedSelectionStart();
+        final int selectionEnd = mConnection.getExpectedSelectionEnd();
+        final int numCharsSelected = selectionEnd - selectionStart;
+        if (numCharsSelected > Constants.MAX_CHARACTERS_FOR_RECAPITALIZATION) {
+            // We bail out if we have too many characters for performance reasons. We don't want
+            // to suck possibly multiple-megabyte data.
+            return;
+        }
         // If we have a recapitalize in progress, use it; otherwise, create a new one.
         if (!mRecapitalizeStatus.isActive()
-                || !mRecapitalizeStatus.isSetAt(mConnection.getExpectedSelectionStart(),
-                        mConnection.getExpectedSelectionEnd())) {
+                || !mRecapitalizeStatus.isSetAt(selectionStart, selectionEnd)) {
             final CharSequence selectedText =
                     mConnection.getSelectedText(0 /* flags, 0 for no styles */);
             if (TextUtils.isEmpty(selectedText)) return; // Race condition with the input connection
-            mRecapitalizeStatus.initialize(mConnection.getExpectedSelectionStart(),
-                    mConnection.getExpectedSelectionEnd(), selectedText.toString(),
+            mRecapitalizeStatus.initialize(selectionStart, selectionEnd, selectedText.toString(),
                     settingsValues.mLocale,
                     settingsValues.mSpacingAndPunctuations.mSortedWordSeparators);
             // We trim leading and trailing whitespace.
@@ -1155,11 +1161,8 @@
         }
         mConnection.finishComposingText();
         mRecapitalizeStatus.rotate();
-        final int numCharsDeleted = mConnection.getExpectedSelectionEnd()
-                - mConnection.getExpectedSelectionStart();
-        mConnection.setSelection(mConnection.getExpectedSelectionEnd(),
-                mConnection.getExpectedSelectionEnd());
-        mConnection.deleteSurroundingText(numCharsDeleted, 0);
+        mConnection.setSelection(selectionEnd, selectionEnd);
+        mConnection.deleteSurroundingText(numCharsSelected, 0);
         mConnection.commitText(mRecapitalizeStatus.getRecapitalizedString(), 0);
         mConnection.setSelection(mRecapitalizeStatus.getNewCursorStart(),
                 mRecapitalizeStatus.getNewCursorEnd());