Merge "Add support for maxlength text fields to WebViewInputConnection."
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 424dd6d..af3bb4d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -378,6 +378,7 @@
         private int mInputType;
         private int mImeOptions;
         private String mHint;
+        private int mMaxLength;
 
         public WebViewInputConnection() {
             super(WebView.this, true);
@@ -412,13 +413,9 @@
             Editable editable = getEditable();
             int selectionStart = Selection.getSelectionStart(editable);
             int selectionEnd = Selection.getSelectionEnd(editable);
+            text = limitReplaceTextByMaxLength(text, editable.length());
             editable.replace(0, editable.length(), text);
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm != null) {
-                // Since the text has changed, do not allow the IME to replace the
-                // existing text as though it were a completion.
-                imm.restartInput(WebView.this);
-            }
+            restartInput();
             // Keep the previous selection.
             selectionStart = Math.min(selectionStart, editable.length());
             selectionEnd = Math.min(selectionEnd, editable.length());
@@ -429,14 +426,10 @@
             Editable editable = getEditable();
             int selectionStart = Selection.getSelectionStart(editable);
             int selectionEnd = Selection.getSelectionEnd(editable);
+            text = limitReplaceTextByMaxLength(text, selectionEnd - selectionStart);
             setNewText(selectionStart, selectionEnd, text);
             editable.replace(selectionStart, selectionEnd, text);
-            InputMethodManager imm = InputMethodManager.peekInstance();
-            if (imm != null) {
-                // Since the text has changed, do not allow the IME to replace the
-                // existing text as though it were a completion.
-                imm.restartInput(WebView.this);
-            }
+            restartInput();
             // Move caret to the end of the new text
             int newCaret = selectionStart + text.length();
             setSelection(newCaret, newCaret);
@@ -456,8 +449,19 @@
                 end = start;
                 start = temp;
             }
-            setNewText(start, end, text);
-            return super.setComposingText(text, newCursorPosition);
+            CharSequence limitedText = limitReplaceTextByMaxLength(text, end - start);
+            setNewText(start, end, limitedText);
+            if (limitedText != text) {
+                newCursorPosition -= text.length() - limitedText.length();
+            }
+            super.setComposingText(limitedText, newCursorPosition);
+            if (limitedText != text) {
+                restartInput();
+                int lastCaret = start + limitedText.length();
+                finishComposingText();
+                setSelection(lastCaret, lastCaret);
+            }
+            return true;
         }
 
         @Override
@@ -573,6 +577,7 @@
             mHint = initData.mLabel;
             mInputType = inputType;
             mImeOptions = imeOptions;
+            mMaxLength = initData.mMaxLength;
         }
 
         public void setupEditorInfo(EditorInfo outAttrs) {
@@ -660,6 +665,29 @@
                     KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                     KeyEvent.FLAG_SOFT_KEYBOARD));
         }
+
+        private CharSequence limitReplaceTextByMaxLength(CharSequence text,
+                int numReplaced) {
+            if (mMaxLength > 0) {
+                Editable editable = getEditable();
+                int maxReplace = mMaxLength - editable.length() + numReplaced;
+                if (maxReplace < text.length()) {
+                    maxReplace = Math.max(maxReplace, 0);
+                    // New length is greater than the maximum. trim it down.
+                    text = text.subSequence(0, maxReplace);
+                }
+            }
+            return text;
+        }
+
+        private void restartInput() {
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) {
+                // Since the text has changed, do not allow the IME to replace the
+                // existing text as though it were a completion.
+                imm.restartInput(WebView.this);
+            }
+        }
     }
 
     private class PastePopupWindow extends PopupWindow implements OnClickListener {
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index e7da1a8..93fd92b 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -923,13 +923,14 @@
     static class TextFieldInitData {
         public TextFieldInitData(int fieldPointer,
                 String text, int type, boolean isSpellCheckEnabled,
-                boolean isTextFieldNext, String label) {
+                boolean isTextFieldNext, String label, int maxLength) {
             mFieldPointer = fieldPointer;
             mText = text;
             mType = type;
             mIsSpellCheckEnabled = isSpellCheckEnabled;
             mIsTextFieldNext = isTextFieldNext;
             mLabel = label;
+            mMaxLength = maxLength;
         }
         int mFieldPointer;
         String mText;
@@ -937,6 +938,7 @@
         boolean mIsSpellCheckEnabled;
         boolean mIsTextFieldNext;
         String mLabel;
+        int mMaxLength;
     }
 
     // mAction of TouchEventData can be MotionEvent.getAction() which uses the
@@ -2826,12 +2828,13 @@
     // called by JNI
     private void initEditField(int pointer, String text, int inputType,
             boolean isSpellCheckEnabled, boolean nextFieldIsText,
-            String label, int start, int end, int selectionPtr) {
+            String label, int start, int end, int selectionPtr, int maxLength) {
         if (mWebView == null) {
             return;
         }
         TextFieldInitData initData = new TextFieldInitData(pointer,
-                text, inputType, isSpellCheckEnabled, nextFieldIsText, label);
+                text, inputType, isSpellCheckEnabled, nextFieldIsText, label,
+                maxLength);
         Message.obtain(mWebView.mPrivateHandler,
                 WebView.INIT_EDIT_FIELD, initData).sendToTarget();
         Message.obtain(mWebView.mPrivateHandler,