Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.webkit;
     18 
     19 import android.accessibilityservice.AccessibilityServiceInfo;
     20 import android.animation.ObjectAnimator;
     21 import android.annotation.Widget;
     22 import android.app.ActivityManager;
     23 import android.app.AlertDialog;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ClipData;
     26 import android.content.ClipboardManager;
     27 import android.content.ComponentCallbacks2;
     28 import android.content.Context;
     29 import android.content.DialogInterface;
     30 import android.content.Intent;
     31 import android.content.IntentFilter;
     32 import android.content.pm.PackageManager;
     33 import android.content.res.Configuration;
     34 import android.database.DataSetObserver;
     35 import android.graphics.Bitmap;
     36 import android.graphics.BitmapFactory;
     37 import android.graphics.BitmapShader;
     38 import android.graphics.Canvas;
     39 import android.graphics.Color;
     40 import android.graphics.ColorFilter;
     41 import android.graphics.DrawFilter;
     42 import android.graphics.Paint;
     43 import android.graphics.PaintFlagsDrawFilter;
     44 import android.graphics.Picture;
     45 import android.graphics.Point;
     46 import android.graphics.PointF;
     47 import android.graphics.Rect;
     48 import android.graphics.RectF;
     49 import android.graphics.Region;
     50 import android.graphics.RegionIterator;
     51 import android.graphics.Shader;
     52 import android.graphics.drawable.Drawable;
     53 import android.net.Proxy;
     54 import android.net.ProxyProperties;
     55 import android.net.Uri;
     56 import android.net.http.SslCertificate;
     57 import android.os.AsyncTask;
     58 import android.os.Build;
     59 import android.os.Bundle;
     60 import android.os.Handler;
     61 import android.os.Looper;
     62 import android.os.Message;
     63 import android.os.SystemClock;
     64 import android.security.KeyChain;
     65 import android.text.Editable;
     66 import android.text.InputType;
     67 import android.text.Selection;
     68 import android.text.TextUtils;
     69 import android.util.DisplayMetrics;
     70 import android.util.EventLog;
     71 import android.util.Log;
     72 import android.view.Gravity;
     73 import android.view.HapticFeedbackConstants;
     74 import android.view.HardwareCanvas;
     75 import android.view.InputDevice;
     76 import android.view.KeyCharacterMap;
     77 import android.view.KeyEvent;
     78 import android.view.LayoutInflater;
     79 import android.view.MotionEvent;
     80 import android.view.ScaleGestureDetector;
     81 import android.view.SoundEffectConstants;
     82 import android.view.VelocityTracker;
     83 import android.view.View;
     84 import android.view.View.MeasureSpec;
     85 import android.view.ViewConfiguration;
     86 import android.view.ViewGroup;
     87 import android.view.ViewParent;
     88 import android.view.ViewRootImpl;
     89 import android.view.accessibility.AccessibilityEvent;
     90 import android.view.accessibility.AccessibilityManager;
     91 import android.view.accessibility.AccessibilityNodeInfo;
     92 import android.view.inputmethod.BaseInputConnection;
     93 import android.view.inputmethod.EditorInfo;
     94 import android.view.inputmethod.InputConnection;
     95 import android.view.inputmethod.InputMethodManager;
     96 import android.webkit.WebView.HitTestResult;
     97 import android.webkit.WebView.PictureListener;
     98 import android.webkit.WebViewCore.DrawData;
     99 import android.webkit.WebViewCore.EventHub;
    100 import android.webkit.WebViewCore.TextFieldInitData;
    101 import android.webkit.WebViewCore.TextSelectionData;
    102 import android.webkit.WebViewCore.WebKitHitTest;
    103 import android.widget.AbsoluteLayout;
    104 import android.widget.Adapter;
    105 import android.widget.AdapterView;
    106 import android.widget.AdapterView.OnItemClickListener;
    107 import android.widget.ArrayAdapter;
    108 import android.widget.CheckedTextView;
    109 import android.widget.LinearLayout;
    110 import android.widget.ListView;
    111 import android.widget.OverScroller;
    112 import android.widget.PopupWindow;
    113 import android.widget.Scroller;
    114 import android.widget.TextView;
    115 import android.widget.Toast;
    116 
    117 import junit.framework.Assert;
    118 
    119 import java.io.BufferedWriter;
    120 import java.io.ByteArrayOutputStream;
    121 import java.io.File;
    122 import java.io.FileInputStream;
    123 import java.io.FileNotFoundException;
    124 import java.io.FileOutputStream;
    125 import java.io.IOException;
    126 import java.io.InputStream;
    127 import java.io.OutputStream;
    128 import java.net.URLDecoder;
    129 import java.util.ArrayList;
    130 import java.util.HashMap;
    131 import java.util.HashSet;
    132 import java.util.List;
    133 import java.util.Locale;
    134 import java.util.Map;
    135 import java.util.Set;
    136 import java.util.Vector;
    137 
    138 /**
    139  * Implements a backend provider for the {@link WebView} public API.
    140  * @hide
    141  */
    142 // TODO: Check if any WebView published API methods are called from within here, and if so
    143 // we should bounce the call out via the proxy to enable any sub-class to override it.
    144 @Widget
    145 @SuppressWarnings("deprecation")
    146 public final class WebViewClassic implements WebViewProvider, WebViewProvider.ScrollDelegate,
    147         WebViewProvider.ViewDelegate {
    148     /**
    149      * InputConnection used for ContentEditable. This captures changes
    150      * to the text and sends them either as key strokes or text changes.
    151      */
    152     class WebViewInputConnection extends BaseInputConnection {
    153         // Used for mapping characters to keys typed.
    154         private KeyCharacterMap mKeyCharacterMap;
    155         private boolean mIsKeySentByMe;
    156         private int mInputType;
    157         private int mImeOptions;
    158         private String mHint;
    159         private int mMaxLength;
    160         private boolean mIsAutoFillable;
    161         private boolean mIsAutoCompleteEnabled;
    162         private String mName;
    163         private int mBatchLevel;
    164 
    165         public WebViewInputConnection() {
    166             super(mWebView, true);
    167         }
    168 
    169         public void setAutoFillable(int queryId) {
    170             mIsAutoFillable = getSettings().getAutoFillEnabled()
    171                     && (queryId != WebTextView.FORM_NOT_AUTOFILLABLE);
    172             int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION;
    173             if (variation != EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD
    174                     && (mIsAutoFillable || mIsAutoCompleteEnabled)) {
    175                 if (mName != null && mName.length() > 0) {
    176                     requestFormData(mName, mFieldPointer, mIsAutoFillable,
    177                             mIsAutoCompleteEnabled);
    178                 }
    179             }
    180         }
    181 
    182         @Override
    183         public boolean beginBatchEdit() {
    184             if (mBatchLevel == 0) {
    185                 beginTextBatch();
    186             }
    187             mBatchLevel++;
    188             return false;
    189         }
    190 
    191         @Override
    192         public boolean endBatchEdit() {
    193             mBatchLevel--;
    194             if (mBatchLevel == 0) {
    195                 commitTextBatch();
    196             }
    197             return false;
    198         }
    199 
    200         public boolean getIsAutoFillable() {
    201             return mIsAutoFillable;
    202         }
    203 
    204         @Override
    205         public boolean sendKeyEvent(KeyEvent event) {
    206             // Some IMEs send key events directly using sendKeyEvents.
    207             // WebViewInputConnection should treat these as text changes.
    208             if (!mIsKeySentByMe) {
    209                 if (event.getAction() == KeyEvent.ACTION_UP) {
    210                     if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
    211                         return deleteSurroundingText(1, 0);
    212                     } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
    213                         return deleteSurroundingText(0, 1);
    214                     } else if (event.getUnicodeChar() != 0){
    215                         String newComposingText =
    216                                 Character.toString((char)event.getUnicodeChar());
    217                         return commitText(newComposingText, 1);
    218                     }
    219                 } else if (event.getAction() == KeyEvent.ACTION_DOWN &&
    220                         (event.getKeyCode() == KeyEvent.KEYCODE_DEL
    221                         || event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL
    222                         || event.getUnicodeChar() != 0)) {
    223                     return true; // only act on action_down
    224                 }
    225             }
    226             return super.sendKeyEvent(event);
    227         }
    228 
    229         public void setTextAndKeepSelection(CharSequence text) {
    230             Editable editable = getEditable();
    231             int selectionStart = Selection.getSelectionStart(editable);
    232             int selectionEnd = Selection.getSelectionEnd(editable);
    233             text = limitReplaceTextByMaxLength(text, editable.length());
    234             editable.replace(0, editable.length(), text);
    235             restartInput();
    236             // Keep the previous selection.
    237             selectionStart = Math.min(selectionStart, editable.length());
    238             selectionEnd = Math.min(selectionEnd, editable.length());
    239             setSelection(selectionStart, selectionEnd);
    240             finishComposingText();
    241         }
    242 
    243         public void replaceSelection(CharSequence text) {
    244             Editable editable = getEditable();
    245             int selectionStart = Selection.getSelectionStart(editable);
    246             int selectionEnd = Selection.getSelectionEnd(editable);
    247             text = limitReplaceTextByMaxLength(text, selectionEnd - selectionStart);
    248             setNewText(selectionStart, selectionEnd, text);
    249             editable.replace(selectionStart, selectionEnd, text);
    250             restartInput();
    251             // Move caret to the end of the new text
    252             int newCaret = selectionStart + text.length();
    253             setSelection(newCaret, newCaret);
    254         }
    255 
    256         @Override
    257         public boolean setComposingText(CharSequence text, int newCursorPosition) {
    258             Editable editable = getEditable();
    259             int start = getComposingSpanStart(editable);
    260             int end = getComposingSpanEnd(editable);
    261             if (start < 0 || end < 0) {
    262                 start = Selection.getSelectionStart(editable);
    263                 end = Selection.getSelectionEnd(editable);
    264             }
    265             if (end < start) {
    266                 int temp = end;
    267                 end = start;
    268                 start = temp;
    269             }
    270             CharSequence limitedText = limitReplaceTextByMaxLength(text, end - start);
    271             setNewText(start, end, limitedText);
    272             if (limitedText != text) {
    273                 newCursorPosition -= text.length() - limitedText.length();
    274             }
    275             super.setComposingText(limitedText, newCursorPosition);
    276             updateSelection();
    277             if (limitedText != text) {
    278                 int lastCaret = start + limitedText.length();
    279                 finishComposingText();
    280                 setSelection(lastCaret, lastCaret);
    281             }
    282             return true;
    283         }
    284 
    285         @Override
    286         public boolean commitText(CharSequence text, int newCursorPosition) {
    287             setComposingText(text, newCursorPosition);
    288             finishComposingText();
    289             return true;
    290         }
    291 
    292         @Override
    293         public boolean deleteSurroundingText(int leftLength, int rightLength) {
    294             // This code is from BaseInputConnection#deleteSurroundText.
    295             // We have to delete the same text in webkit.
    296             Editable content = getEditable();
    297             int a = Selection.getSelectionStart(content);
    298             int b = Selection.getSelectionEnd(content);
    299 
    300             if (a > b) {
    301                 int tmp = a;
    302                 a = b;
    303                 b = tmp;
    304             }
    305 
    306             int ca = getComposingSpanStart(content);
    307             int cb = getComposingSpanEnd(content);
    308             if (cb < ca) {
    309                 int tmp = ca;
    310                 ca = cb;
    311                 cb = tmp;
    312             }
    313             if (ca != -1 && cb != -1) {
    314                 if (ca < a) a = ca;
    315                 if (cb > b) b = cb;
    316             }
    317 
    318             int endDelete = Math.min(content.length(), b + rightLength);
    319             if (endDelete > b) {
    320                 setNewText(b, endDelete, "");
    321             }
    322             int startDelete = Math.max(0, a - leftLength);
    323             if (startDelete < a) {
    324                 setNewText(startDelete, a, "");
    325             }
    326             return super.deleteSurroundingText(leftLength, rightLength);
    327         }
    328 
    329         @Override
    330         public boolean performEditorAction(int editorAction) {
    331 
    332             boolean handled = true;
    333             switch (editorAction) {
    334             case EditorInfo.IME_ACTION_NEXT:
    335                 mWebView.requestFocus(View.FOCUS_FORWARD);
    336                 break;
    337             case EditorInfo.IME_ACTION_PREVIOUS:
    338                 mWebView.requestFocus(View.FOCUS_BACKWARD);
    339                 break;
    340             case EditorInfo.IME_ACTION_DONE:
    341                 WebViewClassic.this.hideSoftKeyboard();
    342                 break;
    343             case EditorInfo.IME_ACTION_GO:
    344             case EditorInfo.IME_ACTION_SEARCH:
    345                 WebViewClassic.this.hideSoftKeyboard();
    346                 String text = getEditable().toString();
    347                 passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_DOWN,
    348                         KeyEvent.KEYCODE_ENTER));
    349                 passToJavaScript(text, new KeyEvent(KeyEvent.ACTION_UP,
    350                         KeyEvent.KEYCODE_ENTER));
    351                 break;
    352 
    353             default:
    354                 handled = super.performEditorAction(editorAction);
    355                 break;
    356             }
    357 
    358             return handled;
    359         }
    360 
    361         public void initEditorInfo(WebViewCore.TextFieldInitData initData) {
    362             int type = initData.mType;
    363             int inputType = InputType.TYPE_CLASS_TEXT
    364                     | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
    365             int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
    366                     | EditorInfo.IME_FLAG_NO_FULLSCREEN;
    367             if (!initData.mIsSpellCheckEnabled) {
    368                 inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
    369             }
    370             if (WebTextView.TEXT_AREA != type) {
    371                 if (initData.mIsTextFieldNext) {
    372                     imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
    373                 }
    374                 if (initData.mIsTextFieldPrev) {
    375                     imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
    376                 }
    377             }
    378             int action = EditorInfo.IME_ACTION_GO;
    379             switch (type) {
    380                 case WebTextView.NORMAL_TEXT_FIELD:
    381                     break;
    382                 case WebTextView.TEXT_AREA:
    383                     inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE
    384                             | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
    385                             | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
    386                     action = EditorInfo.IME_ACTION_NONE;
    387                     break;
    388                 case WebTextView.PASSWORD:
    389                     inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD;
    390                     break;
    391                 case WebTextView.SEARCH:
    392                     action = EditorInfo.IME_ACTION_SEARCH;
    393                     break;
    394                 case WebTextView.EMAIL:
    395                     // inputType needs to be overwritten because of the different text variation.
    396                     inputType = InputType.TYPE_CLASS_TEXT
    397                             | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS;
    398                     break;
    399                 case WebTextView.NUMBER:
    400                     // inputType needs to be overwritten because of the different class.
    401                     inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL
    402                             | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL;
    403                     // Number and telephone do not have both a Tab key and an
    404                     // action, so set the action to NEXT
    405                     break;
    406                 case WebTextView.TELEPHONE:
    407                     // inputType needs to be overwritten because of the different class.
    408                     inputType = InputType.TYPE_CLASS_PHONE;
    409                     break;
    410                 case WebTextView.URL:
    411                     // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so
    412                     // exclude it for now.
    413                     inputType |= InputType.TYPE_TEXT_VARIATION_URI;
    414                     break;
    415                 default:
    416                     break;
    417             }
    418             imeOptions |= action;
    419             mHint = initData.mLabel;
    420             mInputType = inputType;
    421             mImeOptions = imeOptions;
    422             mMaxLength = initData.mMaxLength;
    423             mIsAutoCompleteEnabled = initData.mIsAutoCompleteEnabled;
    424             mName = initData.mName;
    425             mAutoCompletePopup.clearAdapter();
    426         }
    427 
    428         public void setupEditorInfo(EditorInfo outAttrs) {
    429             outAttrs.inputType = mInputType;
    430             outAttrs.imeOptions = mImeOptions;
    431             outAttrs.hintText = mHint;
    432             outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
    433 
    434             Editable editable = getEditable();
    435             int selectionStart = Selection.getSelectionStart(editable);
    436             int selectionEnd = Selection.getSelectionEnd(editable);
    437             if (selectionStart < 0 || selectionEnd < 0) {
    438                 selectionStart = editable.length();
    439                 selectionEnd = selectionStart;
    440             }
    441             outAttrs.initialSelStart = selectionStart;
    442             outAttrs.initialSelEnd = selectionEnd;
    443         }
    444 
    445         @Override
    446         public boolean setSelection(int start, int end) {
    447             boolean result = super.setSelection(start, end);
    448             updateSelection();
    449             return result;
    450         }
    451 
    452         @Override
    453         public boolean setComposingRegion(int start, int end) {
    454             boolean result = super.setComposingRegion(start, end);
    455             updateSelection();
    456             return result;
    457         }
    458 
    459         /**
    460          * Send the selection and composing spans to the IME.
    461          */
    462         private void updateSelection() {
    463             Editable editable = getEditable();
    464             int selectionStart = Selection.getSelectionStart(editable);
    465             int selectionEnd = Selection.getSelectionEnd(editable);
    466             int composingStart = getComposingSpanStart(editable);
    467             int composingEnd = getComposingSpanEnd(editable);
    468             InputMethodManager imm = InputMethodManager.peekInstance();
    469             if (imm != null) {
    470                 imm.updateSelection(mWebView, selectionStart, selectionEnd,
    471                         composingStart, composingEnd);
    472             }
    473         }
    474 
    475         /**
    476          * Sends a text change to webkit indirectly. If it is a single-
    477          * character add or delete, it sends it as a key stroke. If it cannot
    478          * be represented as a key stroke, it sends it as a field change.
    479          * @param start The start offset (inclusive) of the text being changed.
    480          * @param end The end offset (exclusive) of the text being changed.
    481          * @param text The new text to replace the changed text.
    482          */
    483         private void setNewText(int start, int end, CharSequence text) {
    484             mIsKeySentByMe = true;
    485             Editable editable = getEditable();
    486             CharSequence original = editable.subSequence(start, end);
    487             boolean isCharacterAdd = false;
    488             boolean isCharacterDelete = false;
    489             int textLength = text.length();
    490             int originalLength = original.length();
    491             int selectionStart = Selection.getSelectionStart(editable);
    492             int selectionEnd = Selection.getSelectionEnd(editable);
    493             if (selectionStart == selectionEnd) {
    494                 if (textLength > originalLength) {
    495                     isCharacterAdd = (textLength == originalLength + 1)
    496                             && TextUtils.regionMatches(text, 0, original, 0,
    497                                     originalLength);
    498                 } else if (originalLength > textLength) {
    499                     isCharacterDelete = (textLength == originalLength - 1)
    500                             && TextUtils.regionMatches(text, 0, original, 0,
    501                                     textLength);
    502                 }
    503             }
    504             if (isCharacterAdd) {
    505                 sendCharacter(text.charAt(textLength - 1));
    506             } else if (isCharacterDelete) {
    507                 sendKey(KeyEvent.KEYCODE_DEL);
    508             } else if ((textLength != originalLength) ||
    509                     !TextUtils.regionMatches(text, 0, original, 0,
    510                             textLength)) {
    511                 // Send a message so that key strokes and text replacement
    512                 // do not come out of order.
    513                 Message replaceMessage = mPrivateHandler.obtainMessage(
    514                         REPLACE_TEXT, start,  end, text.toString());
    515                 mPrivateHandler.sendMessage(replaceMessage);
    516             }
    517             if (mAutoCompletePopup != null) {
    518                 StringBuilder newText = new StringBuilder();
    519                 newText.append(editable.subSequence(0, start));
    520                 newText.append(text);
    521                 newText.append(editable.subSequence(end, editable.length()));
    522                 mAutoCompletePopup.setText(newText.toString());
    523             }
    524             mIsKeySentByMe = false;
    525         }
    526 
    527         /**
    528          * Send a single character to the WebView as a key down and up event.
    529          * @param c The character to be sent.
    530          */
    531         private void sendCharacter(char c) {
    532             if (mKeyCharacterMap == null) {
    533                 mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
    534             }
    535             char[] chars = new char[1];
    536             chars[0] = c;
    537             KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
    538             if (events != null) {
    539                 for (KeyEvent event : events) {
    540                     sendKeyEvent(event);
    541                 }
    542             } else {
    543                 Message msg = mPrivateHandler.obtainMessage(KEY_PRESS, (int) c, 0);
    544                 mPrivateHandler.sendMessage(msg);
    545             }
    546         }
    547 
    548         /**
    549          * Send a key event for a specific key code, not a standard
    550          * unicode character.
    551          * @param keyCode The key code to send.
    552          */
    553         private void sendKey(int keyCode) {
    554             long eventTime = SystemClock.uptimeMillis();
    555             sendKeyEvent(new KeyEvent(eventTime, eventTime,
    556                     KeyEvent.ACTION_DOWN, keyCode, 0, 0,
    557                     KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
    558                     KeyEvent.FLAG_SOFT_KEYBOARD));
    559             sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
    560                     KeyEvent.ACTION_UP, keyCode, 0, 0,
    561                     KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
    562                     KeyEvent.FLAG_SOFT_KEYBOARD));
    563         }
    564 
    565         private CharSequence limitReplaceTextByMaxLength(CharSequence text,
    566                 int numReplaced) {
    567             if (mMaxLength > 0) {
    568                 Editable editable = getEditable();
    569                 int maxReplace = mMaxLength - editable.length() + numReplaced;
    570                 if (maxReplace < text.length()) {
    571                     maxReplace = Math.max(maxReplace, 0);
    572                     // New length is greater than the maximum. trim it down.
    573                     text = text.subSequence(0, maxReplace);
    574                 }
    575             }
    576             return text;
    577         }
    578 
    579         private void restartInput() {
    580             InputMethodManager imm = InputMethodManager.peekInstance();
    581             if (imm != null) {
    582                 // Since the text has changed, do not allow the IME to replace the
    583                 // existing text as though it were a completion.
    584                 imm.restartInput(mWebView);
    585             }
    586         }
    587     }
    588 
    589     private class PastePopupWindow extends PopupWindow implements View.OnClickListener {
    590         private ViewGroup mContentView;
    591         private TextView mPasteTextView;
    592 
    593         public PastePopupWindow() {
    594             super(mContext, null,
    595                     com.android.internal.R.attr.textSelectHandleWindowStyle);
    596             setClippingEnabled(true);
    597             LinearLayout linearLayout = new LinearLayout(mContext);
    598             linearLayout.setOrientation(LinearLayout.HORIZONTAL);
    599             mContentView = linearLayout;
    600             mContentView.setBackgroundResource(
    601                     com.android.internal.R.drawable.text_edit_paste_window);
    602 
    603             LayoutInflater inflater = (LayoutInflater)mContext.
    604                     getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    605 
    606             ViewGroup.LayoutParams wrapContent = new ViewGroup.LayoutParams(
    607                     ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    608 
    609             mPasteTextView = (TextView) inflater.inflate(
    610                     com.android.internal.R.layout.text_edit_action_popup_text, null);
    611             mPasteTextView.setLayoutParams(wrapContent);
    612             mContentView.addView(mPasteTextView);
    613             mPasteTextView.setText(com.android.internal.R.string.paste);
    614             mPasteTextView.setOnClickListener(this);
    615             this.setContentView(mContentView);
    616         }
    617 
    618         public void show(Point cursorBottom, Point cursorTop,
    619                 int windowLeft, int windowTop) {
    620             measureContent();
    621 
    622             int width = mContentView.getMeasuredWidth();
    623             int height = mContentView.getMeasuredHeight();
    624             int y = cursorTop.y - height;
    625             int x = cursorTop.x - (width / 2);
    626             if (y < windowTop) {
    627                 // There's not enough room vertically, move it below the
    628                 // handle.
    629                 ensureSelectionHandles();
    630                 y = cursorBottom.y + mSelectHandleCenter.getIntrinsicHeight();
    631                 x = cursorBottom.x - (width / 2);
    632             }
    633             if (x < windowLeft) {
    634                 x = windowLeft;
    635             }
    636             if (!isShowing()) {
    637                 showAtLocation(mWebView, Gravity.NO_GRAVITY, x, y);
    638             }
    639             update(x, y, width, height);
    640         }
    641 
    642         public void hide() {
    643             dismiss();
    644         }
    645 
    646         @Override
    647         public void onClick(View view) {
    648             pasteFromClipboard();
    649             selectionDone();
    650         }
    651 
    652         protected void measureContent() {
    653             final DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
    654             mContentView.measure(
    655                     View.MeasureSpec.makeMeasureSpec(displayMetrics.widthPixels,
    656                             View.MeasureSpec.AT_MOST),
    657                     View.MeasureSpec.makeMeasureSpec(displayMetrics.heightPixels,
    658                             View.MeasureSpec.AT_MOST));
    659         }
    660     }
    661 
    662     // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
    663     // the screen all-the-time. Good for profiling our drawing code
    664     static private final boolean AUTO_REDRAW_HACK = false;
    665 
    666     // The rate at which edit text is scrolled in content pixels per millisecond
    667     static private final float TEXT_SCROLL_RATE = 0.01f;
    668 
    669     // The presumed scroll rate for the first scroll of edit text
    670     static private final long TEXT_SCROLL_FIRST_SCROLL_MS = 16;
    671 
    672     // Buffer pixels of the caret rectangle when moving edit text into view
    673     // after resize.
    674     static private final int EDIT_RECT_BUFFER = 10;
    675 
    676     static private final long SELECTION_HANDLE_ANIMATION_MS = 150;
    677 
    678     // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
    679     private boolean mAutoRedraw;
    680 
    681     // Reference to the AlertDialog displayed by InvokeListBox.
    682     // It's used to dismiss the dialog in destroy if not done before.
    683     private AlertDialog mListBoxDialog = null;
    684 
    685     // Reference to the save password dialog so it can be dimissed in
    686     // destroy if not done before.
    687     private AlertDialog mSavePasswordDialog = null;
    688 
    689     static final String LOGTAG = "webview";
    690 
    691     private ZoomManager mZoomManager;
    692 
    693     private final Rect mInvScreenRect = new Rect();
    694     private final Rect mScreenRect = new Rect();
    695     private final RectF mVisibleContentRect = new RectF();
    696     private boolean mIsWebViewVisible = true;
    697     WebViewInputConnection mInputConnection = null;
    698     private int mFieldPointer;
    699     private PastePopupWindow mPasteWindow;
    700     private AutoCompletePopup mAutoCompletePopup;
    701     Rect mEditTextContentBounds = new Rect();
    702     Rect mEditTextContent = new Rect();
    703     int mEditTextLayerId;
    704     boolean mIsEditingText = false;
    705     ArrayList<Message> mBatchedTextChanges = new ArrayList<Message>();
    706     boolean mIsBatchingTextChanges = false;
    707     private long mLastEditScroll = 0;
    708 
    709     private static class OnTrimMemoryListener implements ComponentCallbacks2 {
    710         private static OnTrimMemoryListener sInstance = null;
    711 
    712         static void init(Context c) {
    713             if (sInstance == null) {
    714                 sInstance = new OnTrimMemoryListener(c.getApplicationContext());
    715             }
    716         }
    717 
    718         private OnTrimMemoryListener(Context c) {
    719             c.registerComponentCallbacks(this);
    720         }
    721 
    722         @Override
    723         public void onConfigurationChanged(Configuration newConfig) {
    724             // Ignore
    725         }
    726 
    727         @Override
    728         public void onLowMemory() {
    729             // Ignore
    730         }
    731 
    732         @Override
    733         public void onTrimMemory(int level) {
    734             if (DebugFlags.WEB_VIEW) {
    735                 Log.d("WebView", "onTrimMemory: " + level);
    736             }
    737             // When framework reset EGL context during high memory pressure, all
    738             // the existing GL resources for the html5 video will be destroyed
    739             // at native side.
    740             // Here we just need to clean up the Surface Texture which is static.
    741             if (level > TRIM_MEMORY_UI_HIDDEN) {
    742                 HTML5VideoInline.cleanupSurfaceTexture();
    743                 HTML5VideoView.release();
    744             }
    745             WebViewClassic.nativeOnTrimMemory(level);
    746         }
    747     }
    748 
    749     // A final CallbackProxy shared by WebViewCore and BrowserFrame.
    750     private CallbackProxy mCallbackProxy;
    751 
    752     private WebViewDatabaseClassic mDatabase;
    753 
    754     // SSL certificate for the main top-level page (if secure)
    755     private SslCertificate mCertificate;
    756 
    757     // Native WebView pointer that is 0 until the native object has been
    758     // created.
    759     private int mNativeClass;
    760     // This would be final but it needs to be set to null when the WebView is
    761     // destroyed.
    762     private WebViewCore mWebViewCore;
    763     // Handler for dispatching UI messages.
    764     /* package */ final Handler mPrivateHandler = new PrivateHandler();
    765     // Used to ignore changes to webkit text that arrives to the UI side after
    766     // more key events.
    767     private int mTextGeneration;
    768 
    769     /* package */ void incrementTextGeneration() { mTextGeneration++; }
    770 
    771     // Used by WebViewCore to create child views.
    772     /* package */ ViewManager mViewManager;
    773 
    774     // Used to display in full screen mode
    775     PluginFullScreenHolder mFullScreenHolder;
    776 
    777     /**
    778      * Position of the last touch event in pixels.
    779      * Use integer to prevent loss of dragging delta calculation accuracy;
    780      * which was done in float and converted to integer, and resulted in gradual
    781      * and compounding touch position and view dragging mismatch.
    782      */
    783     private int mLastTouchX;
    784     private int mLastTouchY;
    785     private int mStartTouchX;
    786     private int mStartTouchY;
    787     private float mAverageAngle;
    788 
    789     /**
    790      * Time of the last touch event.
    791      */
    792     private long mLastTouchTime;
    793 
    794     /**
    795      * Time of the last time sending touch event to WebViewCore
    796      */
    797     private long mLastSentTouchTime;
    798 
    799     /**
    800      * The minimum elapsed time before sending another ACTION_MOVE event to
    801      * WebViewCore. This really should be tuned for each type of the devices.
    802      * For example in Google Map api test case, it takes Dream device at least
    803      * 150ms to do a full cycle in the WebViewCore by processing a touch event,
    804      * triggering the layout and drawing the picture. While the same process
    805      * takes 60+ms on the current high speed device. If we make
    806      * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
    807      * to WebViewCore queue and the real layout and draw events will be pushed
    808      * to further, which slows down the refresh rate. Choose 50 to favor the
    809      * current high speed devices. For Dream like devices, 100 is a better
    810      * choice. Maybe make this in the buildspec later.
    811      * (Update 12/14/2010: changed to 0 since current device should be able to
    812      * handle the raw events and Map team voted to have the raw events too.
    813      */
    814     private static final int TOUCH_SENT_INTERVAL = 0;
    815     private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL;
    816 
    817     /**
    818      * Helper class to get velocity for fling
    819      */
    820     VelocityTracker mVelocityTracker;
    821     private int mMaximumFling;
    822     private float mLastVelocity;
    823     private float mLastVelX;
    824     private float mLastVelY;
    825 
    826     // The id of the native layer being scrolled.
    827     private int mCurrentScrollingLayerId;
    828     private Rect mScrollingLayerRect = new Rect();
    829 
    830     // only trigger accelerated fling if the new velocity is at least
    831     // MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION times of the previous velocity
    832     private static final float MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION = 0.2f;
    833 
    834     /**
    835      * Touch mode
    836      * TODO: Some of this is now unnecessary as it is handled by
    837      * WebInputTouchDispatcher (such as click, long press, and double tap).
    838      */
    839     private int mTouchMode = TOUCH_DONE_MODE;
    840     private static final int TOUCH_INIT_MODE = 1;
    841     private static final int TOUCH_DRAG_START_MODE = 2;
    842     private static final int TOUCH_DRAG_MODE = 3;
    843     private static final int TOUCH_SHORTPRESS_START_MODE = 4;
    844     private static final int TOUCH_SHORTPRESS_MODE = 5;
    845     private static final int TOUCH_DOUBLE_TAP_MODE = 6;
    846     private static final int TOUCH_DONE_MODE = 7;
    847     private static final int TOUCH_PINCH_DRAG = 8;
    848     private static final int TOUCH_DRAG_LAYER_MODE = 9;
    849     private static final int TOUCH_DRAG_TEXT_MODE = 10;
    850 
    851     // true when the touch movement exceeds the slop
    852     private boolean mConfirmMove;
    853     private boolean mTouchInEditText;
    854 
    855     // Whether or not to draw the cursor ring.
    856     private boolean mDrawCursorRing = true;
    857 
    858     // true if onPause has been called (and not onResume)
    859     private boolean mIsPaused;
    860 
    861     private HitTestResult mInitialHitTestResult;
    862     private WebKitHitTest mFocusedNode;
    863 
    864     /**
    865      * Customizable constant
    866      */
    867     // pre-computed square of ViewConfiguration.getScaledTouchSlop()
    868     private int mTouchSlopSquare;
    869     // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
    870     private int mDoubleTapSlopSquare;
    871     // pre-computed density adjusted navigation slop
    872     private int mNavSlop;
    873     // This should be ViewConfiguration.getTapTimeout()
    874     // But system time out is 100ms, which is too short for the browser.
    875     // In the browser, if it switches out of tap too soon, jump tap won't work.
    876     // In addition, a double tap on a trackpad will always have a duration of
    877     // 300ms, so this value must be at least that (otherwise we will timeout the
    878     // first tap and convert it to a long press).
    879     private static final int TAP_TIMEOUT = 300;
    880     // This should be ViewConfiguration.getLongPressTimeout()
    881     // But system time out is 500ms, which is too short for the browser.
    882     // With a short timeout, it's difficult to treat trigger a short press.
    883     private static final int LONG_PRESS_TIMEOUT = 1000;
    884     // needed to avoid flinging after a pause of no movement
    885     private static final int MIN_FLING_TIME = 250;
    886     // draw unfiltered after drag is held without movement
    887     private static final int MOTIONLESS_TIME = 100;
    888     // The amount of content to overlap between two screens when going through
    889     // pages with the space bar, in pixels.
    890     private static final int PAGE_SCROLL_OVERLAP = 24;
    891 
    892     /**
    893      * These prevent calling requestLayout if either dimension is fixed. This
    894      * depends on the layout parameters and the measure specs.
    895      */
    896     boolean mWidthCanMeasure;
    897     boolean mHeightCanMeasure;
    898 
    899     // Remember the last dimensions we sent to the native side so we can avoid
    900     // sending the same dimensions more than once.
    901     int mLastWidthSent;
    902     int mLastHeightSent;
    903     // Since view height sent to webkit could be fixed to avoid relayout, this
    904     // value records the last sent actual view height.
    905     int mLastActualHeightSent;
    906 
    907     private int mContentWidth;   // cache of value from WebViewCore
    908     private int mContentHeight;  // cache of value from WebViewCore
    909 
    910     // Need to have the separate control for horizontal and vertical scrollbar
    911     // style than the View's single scrollbar style
    912     private boolean mOverlayHorizontalScrollbar = true;
    913     private boolean mOverlayVerticalScrollbar = false;
    914 
    915     // our standard speed. this way small distances will be traversed in less
    916     // time than large distances, but we cap the duration, so that very large
    917     // distances won't take too long to get there.
    918     private static final int STD_SPEED = 480;  // pixels per second
    919     // time for the longest scroll animation
    920     private static final int MAX_DURATION = 750;   // milliseconds
    921 
    922     // Used by OverScrollGlow
    923     OverScroller mScroller;
    924     Scroller mEditTextScroller;
    925 
    926     private boolean mInOverScrollMode = false;
    927     private static Paint mOverScrollBackground;
    928     private static Paint mOverScrollBorder;
    929 
    930     private boolean mWrapContent;
    931     private static final int MOTIONLESS_FALSE           = 0;
    932     private static final int MOTIONLESS_PENDING         = 1;
    933     private static final int MOTIONLESS_TRUE            = 2;
    934     private static final int MOTIONLESS_IGNORE          = 3;
    935     private int mHeldMotionless;
    936 
    937     // Lazily-instantiated instance for injecting accessibility.
    938     private AccessibilityInjector mAccessibilityInjector;
    939 
    940     /**
    941      * How long the caret handle will last without being touched.
    942      */
    943     private static final long CARET_HANDLE_STAMINA_MS = 3000;
    944 
    945     private Drawable mSelectHandleLeft;
    946     private Drawable mSelectHandleRight;
    947     private Drawable mSelectHandleCenter;
    948     private Point mSelectOffset;
    949     private Point mSelectCursorBase = new Point();
    950     private Rect mSelectHandleBaseBounds = new Rect();
    951     private int mSelectCursorBaseLayerId;
    952     private QuadF mSelectCursorBaseTextQuad = new QuadF();
    953     private Point mSelectCursorExtent = new Point();
    954     private Rect mSelectHandleExtentBounds = new Rect();
    955     private int mSelectCursorExtentLayerId;
    956     private QuadF mSelectCursorExtentTextQuad = new QuadF();
    957     private Point mSelectDraggingCursor;
    958     private QuadF mSelectDraggingTextQuad;
    959     private boolean mIsCaretSelection;
    960     static final int HANDLE_ID_BASE = 0;
    961     static final int HANDLE_ID_EXTENT = 1;
    962 
    963     // the color used to highlight the touch rectangles
    964     static final int HIGHLIGHT_COLOR = 0x6633b5e5;
    965     // the region indicating where the user touched on the screen
    966     private Region mTouchHighlightRegion = new Region();
    967     // the paint for the touch highlight
    968     private Paint mTouchHightlightPaint = new Paint();
    969     // debug only
    970     private static final boolean DEBUG_TOUCH_HIGHLIGHT = true;
    971     private static final int TOUCH_HIGHLIGHT_ELAPSE_TIME = 2000;
    972     private Paint mTouchCrossHairColor;
    973     private int mTouchHighlightX;
    974     private int mTouchHighlightY;
    975     private boolean mShowTapHighlight;
    976 
    977     // Basically this proxy is used to tell the Video to update layer tree at
    978     // SetBaseLayer time and to pause when WebView paused.
    979     private HTML5VideoViewProxy mHTML5VideoViewProxy;
    980 
    981     // If we are using a set picture, don't send view updates to webkit
    982     private boolean mBlockWebkitViewMessages = false;
    983 
    984     // cached value used to determine if we need to switch drawing models
    985     private boolean mHardwareAccelSkia = false;
    986 
    987     /*
    988      * Private message ids
    989      */
    990     private static final int REMEMBER_PASSWORD          = 1;
    991     private static final int NEVER_REMEMBER_PASSWORD    = 2;
    992     private static final int SWITCH_TO_SHORTPRESS       = 3;
    993     private static final int SWITCH_TO_LONGPRESS        = 4;
    994     private static final int RELEASE_SINGLE_TAP         = 5;
    995     private static final int REQUEST_FORM_DATA          = 6;
    996     private static final int DRAG_HELD_MOTIONLESS       = 8;
    997     private static final int PREVENT_DEFAULT_TIMEOUT    = 10;
    998     private static final int SCROLL_SELECT_TEXT         = 11;
    999 
   1000 
   1001     private static final int FIRST_PRIVATE_MSG_ID = REMEMBER_PASSWORD;
   1002     private static final int LAST_PRIVATE_MSG_ID = SCROLL_SELECT_TEXT;
   1003 
   1004     /*
   1005      * Package message ids
   1006      */
   1007     static final int SCROLL_TO_MSG_ID                   = 101;
   1008     static final int NEW_PICTURE_MSG_ID                 = 105;
   1009     static final int WEBCORE_INITIALIZED_MSG_ID         = 107;
   1010     static final int UPDATE_TEXTFIELD_TEXT_MSG_ID       = 108;
   1011     static final int UPDATE_ZOOM_RANGE                  = 109;
   1012     static final int TAKE_FOCUS                         = 110;
   1013     static final int CLEAR_TEXT_ENTRY                   = 111;
   1014     static final int UPDATE_TEXT_SELECTION_MSG_ID       = 112;
   1015     static final int SHOW_RECT_MSG_ID                   = 113;
   1016     static final int LONG_PRESS_CENTER                  = 114;
   1017     static final int PREVENT_TOUCH_ID                   = 115;
   1018     static final int WEBCORE_NEED_TOUCH_EVENTS          = 116;
   1019     // obj=Rect in doc coordinates
   1020     static final int INVAL_RECT_MSG_ID                  = 117;
   1021     static final int REQUEST_KEYBOARD                   = 118;
   1022     static final int SHOW_FULLSCREEN                    = 120;
   1023     static final int HIDE_FULLSCREEN                    = 121;
   1024     static final int UPDATE_MATCH_COUNT                 = 126;
   1025     static final int CENTER_FIT_RECT                    = 127;
   1026     static final int SET_SCROLLBAR_MODES                = 129;
   1027     static final int SELECTION_STRING_CHANGED           = 130;
   1028     static final int HIT_TEST_RESULT                    = 131;
   1029     static final int SAVE_WEBARCHIVE_FINISHED           = 132;
   1030 
   1031     static final int SET_AUTOFILLABLE                   = 133;
   1032     static final int AUTOFILL_COMPLETE                  = 134;
   1033 
   1034     static final int SCREEN_ON                          = 136;
   1035     static final int UPDATE_ZOOM_DENSITY                = 139;
   1036     static final int EXIT_FULLSCREEN_VIDEO              = 140;
   1037 
   1038     static final int COPY_TO_CLIPBOARD                  = 141;
   1039     static final int INIT_EDIT_FIELD                    = 142;
   1040     static final int REPLACE_TEXT                       = 143;
   1041     static final int CLEAR_CARET_HANDLE                 = 144;
   1042     static final int KEY_PRESS                          = 145;
   1043     static final int RELOCATE_AUTO_COMPLETE_POPUP       = 146;
   1044     static final int FOCUS_NODE_CHANGED                 = 147;
   1045     static final int AUTOFILL_FORM                      = 148;
   1046     static final int SCROLL_EDIT_TEXT                   = 149;
   1047     static final int EDIT_TEXT_SIZE_CHANGED             = 150;
   1048     static final int SHOW_CARET_HANDLE                  = 151;
   1049     static final int UPDATE_CONTENT_BOUNDS              = 152;
   1050     static final int SCROLL_HANDLE_INTO_VIEW            = 153;
   1051 
   1052     private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
   1053     private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT;
   1054 
   1055     static final String[] HandlerPrivateDebugString = {
   1056         "REMEMBER_PASSWORD", //              = 1;
   1057         "NEVER_REMEMBER_PASSWORD", //        = 2;
   1058         "SWITCH_TO_SHORTPRESS", //           = 3;
   1059         "SWITCH_TO_LONGPRESS", //            = 4;
   1060         "RELEASE_SINGLE_TAP", //             = 5;
   1061         "REQUEST_FORM_DATA", //              = 6;
   1062         "RESUME_WEBCORE_PRIORITY", //        = 7;
   1063         "DRAG_HELD_MOTIONLESS", //           = 8;
   1064         "", //             = 9;
   1065         "PREVENT_DEFAULT_TIMEOUT", //        = 10;
   1066         "SCROLL_SELECT_TEXT" //              = 11;
   1067     };
   1068 
   1069     static final String[] HandlerPackageDebugString = {
   1070         "SCROLL_TO_MSG_ID", //               = 101;
   1071         "102", //                            = 102;
   1072         "103", //                            = 103;
   1073         "104", //                            = 104;
   1074         "NEW_PICTURE_MSG_ID", //             = 105;
   1075         "UPDATE_TEXT_ENTRY_MSG_ID", //       = 106;
   1076         "WEBCORE_INITIALIZED_MSG_ID", //     = 107;
   1077         "UPDATE_TEXTFIELD_TEXT_MSG_ID", //   = 108;
   1078         "UPDATE_ZOOM_RANGE", //              = 109;
   1079         "UNHANDLED_NAV_KEY", //              = 110;
   1080         "CLEAR_TEXT_ENTRY", //               = 111;
   1081         "UPDATE_TEXT_SELECTION_MSG_ID", //   = 112;
   1082         "SHOW_RECT_MSG_ID", //               = 113;
   1083         "LONG_PRESS_CENTER", //              = 114;
   1084         "PREVENT_TOUCH_ID", //               = 115;
   1085         "WEBCORE_NEED_TOUCH_EVENTS", //      = 116;
   1086         "INVAL_RECT_MSG_ID", //              = 117;
   1087         "REQUEST_KEYBOARD", //               = 118;
   1088         "DO_MOTION_UP", //                   = 119;
   1089         "SHOW_FULLSCREEN", //                = 120;
   1090         "HIDE_FULLSCREEN", //                = 121;
   1091         "DOM_FOCUS_CHANGED", //              = 122;
   1092         "REPLACE_BASE_CONTENT", //           = 123;
   1093         "RETURN_LABEL", //                   = 125;
   1094         "UPDATE_MATCH_COUNT", //             = 126;
   1095         "CENTER_FIT_RECT", //                = 127;
   1096         "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128;
   1097         "SET_SCROLLBAR_MODES", //            = 129;
   1098         "SELECTION_STRING_CHANGED", //       = 130;
   1099         "SET_TOUCH_HIGHLIGHT_RECTS", //      = 131;
   1100         "SAVE_WEBARCHIVE_FINISHED", //       = 132;
   1101         "SET_AUTOFILLABLE", //               = 133;
   1102         "AUTOFILL_COMPLETE", //              = 134;
   1103         "SELECT_AT", //                      = 135;
   1104         "SCREEN_ON", //                      = 136;
   1105         "ENTER_FULLSCREEN_VIDEO", //         = 137;
   1106         "UPDATE_SELECTION", //               = 138;
   1107         "UPDATE_ZOOM_DENSITY" //             = 139;
   1108     };
   1109 
   1110     // If the site doesn't use the viewport meta tag to specify the viewport,
   1111     // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
   1112     static final int DEFAULT_VIEWPORT_WIDTH = 980;
   1113 
   1114     // normally we try to fit the content to the minimum preferred width
   1115     // calculated by the Webkit. To avoid the bad behavior when some site's
   1116     // minimum preferred width keeps growing when changing the viewport width or
   1117     // the minimum preferred width is huge, an upper limit is needed.
   1118     static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
   1119 
   1120     // initial scale in percent. 0 means using default.
   1121     private int mInitialScaleInPercent = 0;
   1122 
   1123     // Whether or not a scroll event should be sent to webkit.  This is only set
   1124     // to false when restoring the scroll position.
   1125     private boolean mSendScrollEvent = true;
   1126 
   1127     private int mSnapScrollMode = SNAP_NONE;
   1128     private static final int SNAP_NONE = 0;
   1129     private static final int SNAP_LOCK = 1; // not a separate state
   1130     private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
   1131     private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
   1132     private boolean mSnapPositive;
   1133 
   1134     // keep these in sync with their counterparts in WebView.cpp
   1135     private static final int DRAW_EXTRAS_NONE = 0;
   1136     private static final int DRAW_EXTRAS_SELECTION = 1;
   1137     private static final int DRAW_EXTRAS_CURSOR_RING = 2;
   1138 
   1139     // keep this in sync with WebCore:ScrollbarMode in WebKit
   1140     private static final int SCROLLBAR_AUTO = 0;
   1141     private static final int SCROLLBAR_ALWAYSOFF = 1;
   1142     // as we auto fade scrollbar, this is ignored.
   1143     private static final int SCROLLBAR_ALWAYSON = 2;
   1144     private int mHorizontalScrollBarMode = SCROLLBAR_AUTO;
   1145     private int mVerticalScrollBarMode = SCROLLBAR_AUTO;
   1146 
   1147     /**
   1148      * Max distance to overscroll by in pixels.
   1149      * This how far content can be pulled beyond its normal bounds by the user.
   1150      */
   1151     private int mOverscrollDistance;
   1152 
   1153     /**
   1154      * Max distance to overfling by in pixels.
   1155      * This is how far flinged content can move beyond the end of its normal bounds.
   1156      */
   1157     private int mOverflingDistance;
   1158 
   1159     private OverScrollGlow mOverScrollGlow;
   1160 
   1161     // Used to match key downs and key ups
   1162     private Vector<Integer> mKeysPressed;
   1163 
   1164     /* package */ static boolean mLogEvent = true;
   1165 
   1166     // for event log
   1167     private long mLastTouchUpTime = 0;
   1168 
   1169     private WebViewCore.AutoFillData mAutoFillData;
   1170 
   1171     private static boolean sNotificationsEnabled = true;
   1172 
   1173     /**
   1174      * URI scheme for telephone number
   1175      */
   1176     public static final String SCHEME_TEL = "tel:";
   1177     /**
   1178      * URI scheme for email address
   1179      */
   1180     public static final String SCHEME_MAILTO = "mailto:";
   1181     /**
   1182      * URI scheme for map address
   1183      */
   1184     public static final String SCHEME_GEO = "geo:0,0?q=";
   1185 
   1186     private int mBackgroundColor = Color.WHITE;
   1187 
   1188     private static final long SELECT_SCROLL_INTERVAL = 1000 / 60; // 60 / second
   1189     private int mAutoScrollX = 0;
   1190     private int mAutoScrollY = 0;
   1191     private int mMinAutoScrollX = 0;
   1192     private int mMaxAutoScrollX = 0;
   1193     private int mMinAutoScrollY = 0;
   1194     private int mMaxAutoScrollY = 0;
   1195     private Rect mScrollingLayerBounds = new Rect();
   1196     private boolean mSentAutoScrollMessage = false;
   1197 
   1198     // used for serializing asynchronously handled touch events.
   1199     private WebViewInputDispatcher mInputDispatcher;
   1200 
   1201     // Used to track whether picture updating was paused due to a window focus change.
   1202     private boolean mPictureUpdatePausedForFocusChange = false;
   1203 
   1204     // Used to notify listeners of a new picture.
   1205     private PictureListener mPictureListener;
   1206 
   1207     // Used to notify listeners about find-on-page results.
   1208     private WebView.FindListener mFindListener;
   1209 
   1210     // Used to prevent resending save password message
   1211     private Message mResumeMsg;
   1212 
   1213     /**
   1214      * Refer to {@link WebView#requestFocusNodeHref(Message)} for more information
   1215      */
   1216     static class FocusNodeHref {
   1217         static final String TITLE = "title";
   1218         static final String URL = "url";
   1219         static final String SRC = "src";
   1220     }
   1221 
   1222     public WebViewClassic(WebView webView, WebView.PrivateAccess privateAccess) {
   1223         mWebView = webView;
   1224         mWebViewPrivate = privateAccess;
   1225         mContext = webView.getContext();
   1226     }
   1227 
   1228     /**
   1229      * See {@link WebViewProvider#init(Map, boolean)}
   1230      */
   1231     @Override
   1232     public void init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
   1233         Context context = mContext;
   1234 
   1235         // Used by the chrome stack to find application paths
   1236         JniUtil.setContext(context);
   1237 
   1238         mCallbackProxy = new CallbackProxy(context, this);
   1239         mViewManager = new ViewManager(this);
   1240         L10nUtils.setApplicationContext(context.getApplicationContext());
   1241         mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces);
   1242         mDatabase = WebViewDatabaseClassic.getInstance(context);
   1243         mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel
   1244         mZoomManager = new ZoomManager(this, mCallbackProxy);
   1245 
   1246         /* The init method must follow the creation of certain member variables,
   1247          * such as the mZoomManager.
   1248          */
   1249         init();
   1250         setupPackageListener(context);
   1251         setupProxyListener(context);
   1252         setupTrustStorageListener(context);
   1253         updateMultiTouchSupport(context);
   1254 
   1255         if (privateBrowsing) {
   1256             startPrivateBrowsing();
   1257         }
   1258 
   1259         mAutoFillData = new WebViewCore.AutoFillData();
   1260         mEditTextScroller = new Scroller(context);
   1261     }
   1262 
   1263     // WebViewProvider bindings
   1264 
   1265     static class Factory implements WebViewFactoryProvider,  WebViewFactoryProvider.Statics {
   1266         @Override
   1267         public String findAddress(String addr) {
   1268             return WebViewClassic.findAddress(addr);
   1269         }
   1270         @Override
   1271         public void setPlatformNotificationsEnabled(boolean enable) {
   1272             if (enable) {
   1273                 WebViewClassic.enablePlatformNotifications();
   1274             } else {
   1275                 WebViewClassic.disablePlatformNotifications();
   1276             }
   1277         }
   1278 
   1279         @Override
   1280         public Statics getStatics() { return this; }
   1281 
   1282         @Override
   1283         public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
   1284             return new WebViewClassic(webView, privateAccess);
   1285         }
   1286 
   1287         @Override
   1288         public GeolocationPermissions getGeolocationPermissions() {
   1289             return GeolocationPermissionsClassic.getInstance();
   1290         }
   1291 
   1292         @Override
   1293         public CookieManager getCookieManager() {
   1294             return CookieManagerClassic.getInstance();
   1295         }
   1296 
   1297         @Override
   1298         public WebIconDatabase getWebIconDatabase() {
   1299             return WebIconDatabaseClassic.getInstance();
   1300         }
   1301 
   1302         @Override
   1303         public WebStorage getWebStorage() {
   1304             return WebStorageClassic.getInstance();
   1305         }
   1306 
   1307         @Override
   1308         public WebViewDatabase getWebViewDatabase(Context context) {
   1309             return WebViewDatabaseClassic.getInstance(context);
   1310         }
   1311 
   1312         @Override
   1313         public String getDefaultUserAgent(Context context) {
   1314             return WebSettingsClassic.getDefaultUserAgentForLocale(context,
   1315                     Locale.getDefault());
   1316         }
   1317     }
   1318 
   1319     private void onHandleUiEvent(MotionEvent event, int eventType, int flags) {
   1320         switch (eventType) {
   1321         case WebViewInputDispatcher.EVENT_TYPE_LONG_PRESS:
   1322             HitTestResult hitTest = getHitTestResult();
   1323             if (hitTest != null) {
   1324                 mWebView.performLongClick();
   1325             }
   1326             break;
   1327         case WebViewInputDispatcher.EVENT_TYPE_DOUBLE_TAP:
   1328             mZoomManager.handleDoubleTap(event.getX(), event.getY());
   1329             break;
   1330         case WebViewInputDispatcher.EVENT_TYPE_TOUCH:
   1331             onHandleUiTouchEvent(event);
   1332             break;
   1333         case WebViewInputDispatcher.EVENT_TYPE_CLICK:
   1334             if (mFocusedNode != null && mFocusedNode.mIntentUrl != null) {
   1335                 mWebView.playSoundEffect(SoundEffectConstants.CLICK);
   1336                 overrideLoading(mFocusedNode.mIntentUrl);
   1337             }
   1338             break;
   1339         }
   1340     }
   1341 
   1342     private void onHandleUiTouchEvent(MotionEvent ev) {
   1343         final ScaleGestureDetector detector =
   1344                 mZoomManager.getScaleGestureDetector();
   1345 
   1346         int action = ev.getActionMasked();
   1347         final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
   1348         final boolean configChanged =
   1349             action == MotionEvent.ACTION_POINTER_UP ||
   1350             action == MotionEvent.ACTION_POINTER_DOWN;
   1351         final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
   1352 
   1353         // Determine focal point
   1354         float sumX = 0, sumY = 0;
   1355         final int count = ev.getPointerCount();
   1356         for (int i = 0; i < count; i++) {
   1357             if (skipIndex == i) continue;
   1358             sumX += ev.getX(i);
   1359             sumY += ev.getY(i);
   1360         }
   1361         final int div = pointerUp ? count - 1 : count;
   1362         float x = sumX / div;
   1363         float y = sumY / div;
   1364 
   1365         if (configChanged) {
   1366             mLastTouchX = Math.round(x);
   1367             mLastTouchY = Math.round(y);
   1368             mLastTouchTime = ev.getEventTime();
   1369             mWebView.cancelLongPress();
   1370             mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
   1371         }
   1372 
   1373         if (detector != null) {
   1374             detector.onTouchEvent(ev);
   1375             if (detector.isInProgress()) {
   1376                 mLastTouchTime = ev.getEventTime();
   1377 
   1378                 if (!mZoomManager.supportsPanDuringZoom()) {
   1379                     return;
   1380                 }
   1381                 mTouchMode = TOUCH_DRAG_MODE;
   1382                 if (mVelocityTracker == null) {
   1383                     mVelocityTracker = VelocityTracker.obtain();
   1384                 }
   1385             }
   1386         }
   1387 
   1388         if (action == MotionEvent.ACTION_POINTER_DOWN) {
   1389             cancelTouch();
   1390             action = MotionEvent.ACTION_DOWN;
   1391         } else if (action == MotionEvent.ACTION_MOVE) {
   1392             // negative x or y indicate it is on the edge, skip it.
   1393             if (x < 0 || y < 0) {
   1394                 return;
   1395             }
   1396         }
   1397 
   1398         handleTouchEventCommon(ev, action, Math.round(x), Math.round(y));
   1399     }
   1400 
   1401     // The webview that is bound to this WebViewClassic instance. Primarily needed for supplying
   1402     // as the first param in the WebViewClient and WebChromeClient callbacks.
   1403     final private WebView mWebView;
   1404     // Callback interface, provides priviledged access into the WebView instance.
   1405     final private WebView.PrivateAccess mWebViewPrivate;
   1406     // Cached reference to mWebView.getContext(), for convenience.
   1407     final private Context mContext;
   1408 
   1409     /**
   1410      * @return The webview proxy that this classic webview is bound to.
   1411      */
   1412     public WebView getWebView() {
   1413         return mWebView;
   1414     }
   1415 
   1416     @Override
   1417     public ViewDelegate getViewDelegate() {
   1418         return this;
   1419     }
   1420 
   1421     @Override
   1422     public ScrollDelegate getScrollDelegate() {
   1423         return this;
   1424     }
   1425 
   1426     public static WebViewClassic fromWebView(WebView webView) {
   1427         return webView == null ? null : (WebViewClassic) webView.getWebViewProvider();
   1428     }
   1429 
   1430     // Accessors, purely for convenience (and to reduce code churn during webview proxy migration).
   1431     int getScrollX() {
   1432         return mWebView.getScrollX();
   1433     }
   1434 
   1435     int getScrollY() {
   1436         return mWebView.getScrollY();
   1437     }
   1438 
   1439     int getWidth() {
   1440         return mWebView.getWidth();
   1441     }
   1442 
   1443     int getHeight() {
   1444         return mWebView.getHeight();
   1445     }
   1446 
   1447     Context getContext() {
   1448         return mContext;
   1449     }
   1450 
   1451     void invalidate() {
   1452         mWebView.invalidate();
   1453     }
   1454 
   1455     // Setters for the Scroll X & Y, without invoking the onScrollChanged etc code paths.
   1456     void setScrollXRaw(int mScrollX) {
   1457         mWebViewPrivate.setScrollXRaw(mScrollX);
   1458     }
   1459 
   1460     void setScrollYRaw(int mScrollY) {
   1461         mWebViewPrivate.setScrollYRaw(mScrollY);
   1462     }
   1463 
   1464     private static class TrustStorageListener extends BroadcastReceiver {
   1465         @Override
   1466         public void onReceive(Context context, Intent intent) {
   1467             if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
   1468                 handleCertTrustChanged();
   1469             }
   1470         }
   1471     }
   1472     private static TrustStorageListener sTrustStorageListener;
   1473 
   1474     /**
   1475      * Handles update to the trust storage.
   1476      */
   1477     private static void handleCertTrustChanged() {
   1478         // send a message for indicating trust storage change
   1479         WebViewCore.sendStaticMessage(EventHub.TRUST_STORAGE_UPDATED, null);
   1480     }
   1481 
   1482     /*
   1483      * @param context This method expects this to be a valid context.
   1484      */
   1485     private static void setupTrustStorageListener(Context context) {
   1486         if (sTrustStorageListener != null ) {
   1487             return;
   1488         }
   1489         IntentFilter filter = new IntentFilter();
   1490         filter.addAction(KeyChain.ACTION_STORAGE_CHANGED);
   1491         sTrustStorageListener = new TrustStorageListener();
   1492         Intent current =
   1493             context.getApplicationContext().registerReceiver(sTrustStorageListener, filter);
   1494         if (current != null) {
   1495             handleCertTrustChanged();
   1496         }
   1497     }
   1498 
   1499     private static class ProxyReceiver extends BroadcastReceiver {
   1500         @Override
   1501         public void onReceive(Context context, Intent intent) {
   1502             if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) {
   1503                 handleProxyBroadcast(intent);
   1504             }
   1505         }
   1506     }
   1507 
   1508     /*
   1509      * Receiver for PROXY_CHANGE_ACTION, will be null when it is not added handling broadcasts.
   1510      */
   1511     private static ProxyReceiver sProxyReceiver;
   1512 
   1513     /*
   1514      * @param context This method expects this to be a valid context
   1515      */
   1516     private static synchronized void setupProxyListener(Context context) {
   1517         if (sProxyReceiver != null || sNotificationsEnabled == false) {
   1518             return;
   1519         }
   1520         IntentFilter filter = new IntentFilter();
   1521         filter.addAction(Proxy.PROXY_CHANGE_ACTION);
   1522         sProxyReceiver = new ProxyReceiver();
   1523         Intent currentProxy = context.getApplicationContext().registerReceiver(
   1524                 sProxyReceiver, filter);
   1525         if (currentProxy != null) {
   1526             handleProxyBroadcast(currentProxy);
   1527         }
   1528     }
   1529 
   1530     /*
   1531      * @param context This method expects this to be a valid context
   1532      */
   1533     private static synchronized void disableProxyListener(Context context) {
   1534         if (sProxyReceiver == null)
   1535             return;
   1536 
   1537         context.getApplicationContext().unregisterReceiver(sProxyReceiver);
   1538         sProxyReceiver = null;
   1539     }
   1540 
   1541     private static void handleProxyBroadcast(Intent intent) {
   1542         ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
   1543         if (proxyProperties == null || proxyProperties.getHost() == null) {
   1544             WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, null);
   1545             return;
   1546         }
   1547         WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, proxyProperties);
   1548     }
   1549 
   1550     /*
   1551      * A variable to track if there is a receiver added for ACTION_PACKAGE_ADDED
   1552      * or ACTION_PACKAGE_REMOVED.
   1553      */
   1554     private static boolean sPackageInstallationReceiverAdded = false;
   1555 
   1556     /*
   1557      * A set of Google packages we monitor for the
   1558      * navigator.isApplicationInstalled() API. Add additional packages as
   1559      * needed.
   1560      */
   1561     private static Set<String> sGoogleApps;
   1562     static {
   1563         sGoogleApps = new HashSet<String>();
   1564         sGoogleApps.add("com.google.android.youtube");
   1565     }
   1566 
   1567     private static class PackageListener extends BroadcastReceiver {
   1568         @Override
   1569         public void onReceive(Context context, Intent intent) {
   1570             final String action = intent.getAction();
   1571             final String packageName = intent.getData().getSchemeSpecificPart();
   1572             final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
   1573             if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
   1574                 // if it is replacing, refreshPlugins() when adding
   1575                 return;
   1576             }
   1577 
   1578             if (sGoogleApps.contains(packageName)) {
   1579                 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
   1580                     WebViewCore.sendStaticMessage(EventHub.ADD_PACKAGE_NAME, packageName);
   1581                 } else {
   1582                     WebViewCore.sendStaticMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
   1583                 }
   1584             }
   1585 
   1586             PluginManager pm = PluginManager.getInstance(context);
   1587             if (pm.containsPluginPermissionAndSignatures(packageName)) {
   1588                 pm.refreshPlugins(Intent.ACTION_PACKAGE_ADDED.equals(action));
   1589             }
   1590         }
   1591     }
   1592 
   1593     private void setupPackageListener(Context context) {
   1594 
   1595         /*
   1596          * we must synchronize the instance check and the creation of the
   1597          * receiver to ensure that only ONE receiver exists for all WebView
   1598          * instances.
   1599          */
   1600         synchronized (WebViewClassic.class) {
   1601 
   1602             // if the receiver already exists then we do not need to register it
   1603             // again
   1604             if (sPackageInstallationReceiverAdded) {
   1605                 return;
   1606             }
   1607 
   1608             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
   1609             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
   1610             filter.addDataScheme("package");
   1611             BroadcastReceiver packageListener = new PackageListener();
   1612             context.getApplicationContext().registerReceiver(packageListener, filter);
   1613             sPackageInstallationReceiverAdded = true;
   1614         }
   1615 
   1616         // check if any of the monitored apps are already installed
   1617         AsyncTask<Void, Void, Set<String>> task = new AsyncTask<Void, Void, Set<String>>() {
   1618 
   1619             @Override
   1620             protected Set<String> doInBackground(Void... unused) {
   1621                 Set<String> installedPackages = new HashSet<String>();
   1622                 PackageManager pm = mContext.getPackageManager();
   1623                 for (String name : sGoogleApps) {
   1624                     try {
   1625                         pm.getPackageInfo(name,
   1626                                 PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
   1627                         installedPackages.add(name);
   1628                     } catch (PackageManager.NameNotFoundException e) {
   1629                         // package not found
   1630                     }
   1631                 }
   1632                 return installedPackages;
   1633             }
   1634 
   1635             // Executes on the UI thread
   1636             @Override
   1637             protected void onPostExecute(Set<String> installedPackages) {
   1638                 if (mWebViewCore != null) {
   1639                     mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, installedPackages);
   1640                 }
   1641             }
   1642         };
   1643         task.execute();
   1644     }
   1645 
   1646     void updateMultiTouchSupport(Context context) {
   1647         mZoomManager.updateMultiTouchSupport(context);
   1648     }
   1649 
   1650     void updateJavaScriptEnabled(boolean enabled) {
   1651         if (isAccessibilityInjectionEnabled()) {
   1652             getAccessibilityInjector().updateJavaScriptEnabled(enabled);
   1653         }
   1654     }
   1655 
   1656     private void init() {
   1657         OnTrimMemoryListener.init(mContext);
   1658         mWebView.setWillNotDraw(false);
   1659         mWebView.setClickable(true);
   1660         mWebView.setLongClickable(true);
   1661 
   1662         final ViewConfiguration configuration = ViewConfiguration.get(mContext);
   1663         int slop = configuration.getScaledTouchSlop();
   1664         mTouchSlopSquare = slop * slop;
   1665         slop = configuration.getScaledDoubleTapSlop();
   1666         mDoubleTapSlopSquare = slop * slop;
   1667         final float density = WebViewCore.getFixedDisplayDensity(mContext);
   1668         // use one line height, 16 based on our current default font, for how
   1669         // far we allow a touch be away from the edge of a link
   1670         mNavSlop = (int) (16 * density);
   1671         mZoomManager.init(density);
   1672         mMaximumFling = configuration.getScaledMaximumFlingVelocity();
   1673 
   1674         // Compute the inverse of the density squared.
   1675         DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
   1676 
   1677         mOverscrollDistance = configuration.getScaledOverscrollDistance();
   1678         mOverflingDistance = configuration.getScaledOverflingDistance();
   1679 
   1680         setScrollBarStyle(mWebViewPrivate.super_getScrollBarStyle());
   1681         // Initially use a size of two, since the user is likely to only hold
   1682         // down two keys at a time (shift + another key)
   1683         mKeysPressed = new Vector<Integer>(2);
   1684         mHTML5VideoViewProxy = null ;
   1685     }
   1686 
   1687     @Override
   1688     public boolean shouldDelayChildPressedState() {
   1689         return true;
   1690     }
   1691 
   1692     @Override
   1693     public boolean performAccessibilityAction(int action, Bundle arguments) {
   1694         if (!mWebView.isEnabled()) {
   1695             // Only default actions are supported while disabled.
   1696             return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
   1697         }
   1698 
   1699         if (getAccessibilityInjector().supportsAccessibilityAction(action)) {
   1700             return getAccessibilityInjector().performAccessibilityAction(action, arguments);
   1701         }
   1702 
   1703         switch (action) {
   1704             case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
   1705             case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
   1706                 final int convertedContentHeight = contentToViewY(getContentHeight());
   1707                 final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
   1708                         - mWebView.getPaddingBottom();
   1709                 final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
   1710                 final boolean canScrollBackward = (getScrollY() > 0);
   1711                 final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
   1712                 if ((action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) && canScrollBackward) {
   1713                     mWebView.scrollBy(0, adjustedViewHeight);
   1714                     return true;
   1715                 }
   1716                 if ((action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) && canScrollForward) {
   1717                     mWebView.scrollBy(0, -adjustedViewHeight);
   1718                     return true;
   1719                 }
   1720                 return false;
   1721             }
   1722         }
   1723 
   1724         return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
   1725     }
   1726 
   1727     @Override
   1728     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
   1729         if (!mWebView.isEnabled()) {
   1730             // Only default actions are supported while disabled.
   1731             return;
   1732         }
   1733 
   1734         info.setScrollable(isScrollableForAccessibility());
   1735 
   1736         final int convertedContentHeight = contentToViewY(getContentHeight());
   1737         final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
   1738                 - mWebView.getPaddingBottom();
   1739         final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
   1740         final boolean canScrollBackward = (getScrollY() > 0);
   1741         final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
   1742 
   1743         if (canScrollForward) {
   1744             info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
   1745         }
   1746 
   1747         if (canScrollForward) {
   1748             info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
   1749         }
   1750 
   1751         getAccessibilityInjector().onInitializeAccessibilityNodeInfo(info);
   1752     }
   1753 
   1754     @Override
   1755     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
   1756         event.setScrollable(isScrollableForAccessibility());
   1757         event.setScrollX(getScrollX());
   1758         event.setScrollY(getScrollY());
   1759         final int convertedContentWidth = contentToViewX(getContentWidth());
   1760         final int adjustedViewWidth = getWidth() - mWebView.getPaddingLeft()
   1761                 - mWebView.getPaddingLeft();
   1762         event.setMaxScrollX(Math.max(convertedContentWidth - adjustedViewWidth, 0));
   1763         final int convertedContentHeight = contentToViewY(getContentHeight());
   1764         final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
   1765                 - mWebView.getPaddingBottom();
   1766         event.setMaxScrollY(Math.max(convertedContentHeight - adjustedViewHeight, 0));
   1767     }
   1768 
   1769     private boolean isAccessibilityInjectionEnabled() {
   1770         final AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
   1771         if (!manager.isEnabled()) {
   1772             return false;
   1773         }
   1774 
   1775         // Accessibility scripts should be injected only when a speaking service
   1776         // is enabled. This may need to change later to accommodate Braille.
   1777         final List<AccessibilityServiceInfo> services = manager.getEnabledAccessibilityServiceList(
   1778                 AccessibilityServiceInfo.FEEDBACK_SPOKEN);
   1779         if (services.isEmpty()) {
   1780             return false;
   1781         }
   1782 
   1783         return true;
   1784     }
   1785 
   1786     private AccessibilityInjector getAccessibilityInjector() {
   1787         if (mAccessibilityInjector == null) {
   1788             mAccessibilityInjector = new AccessibilityInjector(this);
   1789         }
   1790         return mAccessibilityInjector;
   1791     }
   1792 
   1793     private boolean isScrollableForAccessibility() {
   1794         return (contentToViewX(getContentWidth()) > getWidth() - mWebView.getPaddingLeft()
   1795                 - mWebView.getPaddingRight()
   1796                 || contentToViewY(getContentHeight()) > getHeight() - mWebView.getPaddingTop()
   1797                 - mWebView.getPaddingBottom());
   1798     }
   1799 
   1800     @Override
   1801     public void setOverScrollMode(int mode) {
   1802         if (mode != View.OVER_SCROLL_NEVER) {
   1803             if (mOverScrollGlow == null) {
   1804                 mOverScrollGlow = new OverScrollGlow(this);
   1805             }
   1806         } else {
   1807             mOverScrollGlow = null;
   1808         }
   1809     }
   1810 
   1811     /* package */ void adjustDefaultZoomDensity(int zoomDensity) {
   1812         final float density = WebViewCore.getFixedDisplayDensity(mContext)
   1813                 * 100 / zoomDensity;
   1814         updateDefaultZoomDensity(density);
   1815     }
   1816 
   1817     /* package */ void updateDefaultZoomDensity(float density) {
   1818         mNavSlop = (int) (16 * density);
   1819         mZoomManager.updateDefaultZoomDensity(density);
   1820     }
   1821 
   1822     /* package */ int getScaledNavSlop() {
   1823         return viewToContentDimension(mNavSlop);
   1824     }
   1825 
   1826     /* package */ boolean onSavePassword(String schemePlusHost, String username,
   1827             String password, final Message resumeMsg) {
   1828         boolean rVal = false;
   1829         if (resumeMsg == null) {
   1830             // null resumeMsg implies saving password silently
   1831             mDatabase.setUsernamePassword(schemePlusHost, username, password);
   1832         } else {
   1833             if (mResumeMsg != null) {
   1834                 Log.w(LOGTAG, "onSavePassword should not be called while dialog is up");
   1835                 resumeMsg.sendToTarget();
   1836                 return true;
   1837             }
   1838             mResumeMsg = resumeMsg;
   1839             final Message remember = mPrivateHandler.obtainMessage(
   1840                     REMEMBER_PASSWORD);
   1841             remember.getData().putString("host", schemePlusHost);
   1842             remember.getData().putString("username", username);
   1843             remember.getData().putString("password", password);
   1844             remember.obj = resumeMsg;
   1845 
   1846             final Message neverRemember = mPrivateHandler.obtainMessage(
   1847                     NEVER_REMEMBER_PASSWORD);
   1848             neverRemember.getData().putString("host", schemePlusHost);
   1849             neverRemember.getData().putString("username", username);
   1850             neverRemember.getData().putString("password", password);
   1851             neverRemember.obj = resumeMsg;
   1852 
   1853             mSavePasswordDialog = new AlertDialog.Builder(mContext)
   1854                     .setTitle(com.android.internal.R.string.save_password_label)
   1855                     .setMessage(com.android.internal.R.string.save_password_message)
   1856                     .setPositiveButton(com.android.internal.R.string.save_password_notnow,
   1857                     new DialogInterface.OnClickListener() {
   1858                         @Override
   1859                         public void onClick(DialogInterface dialog, int which) {
   1860                             if (mResumeMsg != null) {
   1861                                 resumeMsg.sendToTarget();
   1862                                 mResumeMsg = null;
   1863                             }
   1864                             mSavePasswordDialog = null;
   1865                         }
   1866                     })
   1867                     .setNeutralButton(com.android.internal.R.string.save_password_remember,
   1868                     new DialogInterface.OnClickListener() {
   1869                         @Override
   1870                         public void onClick(DialogInterface dialog, int which) {
   1871                             if (mResumeMsg != null) {
   1872                                 remember.sendToTarget();
   1873                                 mResumeMsg = null;
   1874                             }
   1875                             mSavePasswordDialog = null;
   1876                         }
   1877                     })
   1878                     .setNegativeButton(com.android.internal.R.string.save_password_never,
   1879                     new DialogInterface.OnClickListener() {
   1880                         @Override
   1881                         public void onClick(DialogInterface dialog, int which) {
   1882                             if (mResumeMsg != null) {
   1883                                 neverRemember.sendToTarget();
   1884                                 mResumeMsg = null;
   1885                             }
   1886                             mSavePasswordDialog = null;
   1887                         }
   1888                     })
   1889                     .setOnDismissListener(new DialogInterface.OnDismissListener() {
   1890                         @Override
   1891                         public void onDismiss(DialogInterface dialog) {
   1892                             if (mResumeMsg != null) {
   1893                                 resumeMsg.sendToTarget();
   1894                                 mResumeMsg = null;
   1895                             }
   1896                             mSavePasswordDialog = null;
   1897                         }
   1898                     }).show();
   1899             // Return true so that WebViewCore will pause while the dialog is
   1900             // up.
   1901             rVal = true;
   1902         }
   1903         return rVal;
   1904     }
   1905 
   1906     @Override
   1907     public void setScrollBarStyle(int style) {
   1908         if (style == View.SCROLLBARS_INSIDE_INSET
   1909                 || style == View.SCROLLBARS_OUTSIDE_INSET) {
   1910             mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
   1911         } else {
   1912             mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
   1913         }
   1914     }
   1915 
   1916     /**
   1917      * See {@link WebView#setHorizontalScrollbarOverlay(boolean)}
   1918      */
   1919     @Override
   1920     public void setHorizontalScrollbarOverlay(boolean overlay) {
   1921         mOverlayHorizontalScrollbar = overlay;
   1922     }
   1923 
   1924     /**
   1925      * See {@link WebView#setVerticalScrollbarOverlay(boolean)
   1926      */
   1927     @Override
   1928     public void setVerticalScrollbarOverlay(boolean overlay) {
   1929         mOverlayVerticalScrollbar = overlay;
   1930     }
   1931 
   1932     /**
   1933      * See {@link WebView#overlayHorizontalScrollbar()}
   1934      */
   1935     @Override
   1936     public boolean overlayHorizontalScrollbar() {
   1937         return mOverlayHorizontalScrollbar;
   1938     }
   1939 
   1940     /**
   1941      * See {@link WebView#overlayVerticalScrollbar()}
   1942      */
   1943     @Override
   1944     public boolean overlayVerticalScrollbar() {
   1945         return mOverlayVerticalScrollbar;
   1946     }
   1947 
   1948     /*
   1949      * Return the width of the view where the content of WebView should render
   1950      * to.
   1951      * Note: this can be called from WebCoreThread.
   1952      */
   1953     /* package */ int getViewWidth() {
   1954         if (!mWebView.isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
   1955             return getWidth();
   1956         } else {
   1957             return Math.max(0, getWidth() - mWebView.getVerticalScrollbarWidth());
   1958         }
   1959     }
   1960 
   1961     // Interface to enable the browser to override title bar handling.
   1962     public interface TitleBarDelegate {
   1963         int getTitleHeight();
   1964         public void onSetEmbeddedTitleBar(final View title);
   1965     }
   1966 
   1967     /**
   1968      * Returns the height (in pixels) of the embedded title bar (if any). Does not care about
   1969      * scrolling
   1970      */
   1971     protected int getTitleHeight() {
   1972         if (mWebView instanceof TitleBarDelegate) {
   1973             return ((TitleBarDelegate) mWebView).getTitleHeight();
   1974         }
   1975         return 0;
   1976     }
   1977 
   1978     /**
   1979      * See {@link WebView#getVisibleTitleHeight()}
   1980      */
   1981     @Override
   1982     @Deprecated
   1983     public int getVisibleTitleHeight() {
   1984         // Actually, this method returns the height of the embedded title bar if one is set via the
   1985         // hidden setEmbeddedTitleBar method.
   1986         return getVisibleTitleHeightImpl();
   1987     }
   1988 
   1989     private int getVisibleTitleHeightImpl() {
   1990         // need to restrict mScrollY due to over scroll
   1991         return Math.max(getTitleHeight() - Math.max(0, getScrollY()),
   1992                 getOverlappingActionModeHeight());
   1993     }
   1994 
   1995     private int mCachedOverlappingActionModeHeight = -1;
   1996 
   1997     private int getOverlappingActionModeHeight() {
   1998         if (mFindCallback == null) {
   1999             return 0;
   2000         }
   2001         if (mCachedOverlappingActionModeHeight < 0) {
   2002             mWebView.getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
   2003             mCachedOverlappingActionModeHeight = Math.max(0,
   2004                     mFindCallback.getActionModeGlobalBottom() - mGlobalVisibleRect.top);
   2005         }
   2006         return mCachedOverlappingActionModeHeight;
   2007     }
   2008 
   2009     /*
   2010      * Return the height of the view where the content of WebView should render
   2011      * to.  Note that this excludes mTitleBar, if there is one.
   2012      * Note: this can be called from WebCoreThread.
   2013      */
   2014     /* package */ int getViewHeight() {
   2015         return getViewHeightWithTitle() - getVisibleTitleHeightImpl();
   2016     }
   2017 
   2018     int getViewHeightWithTitle() {
   2019         int height = getHeight();
   2020         if (mWebView.isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
   2021             height -= mWebViewPrivate.getHorizontalScrollbarHeight();
   2022         }
   2023         return height;
   2024     }
   2025 
   2026     /**
   2027      * See {@link WebView#getCertificate()}
   2028      */
   2029     @Override
   2030     public SslCertificate getCertificate() {
   2031         return mCertificate;
   2032     }
   2033 
   2034     /**
   2035      * See {@link WebView#setCertificate(SslCertificate)}
   2036      */
   2037     @Override
   2038     public void setCertificate(SslCertificate certificate) {
   2039         if (DebugFlags.WEB_VIEW) {
   2040             Log.v(LOGTAG, "setCertificate=" + certificate);
   2041         }
   2042         // here, the certificate can be null (if the site is not secure)
   2043         mCertificate = certificate;
   2044     }
   2045 
   2046     //-------------------------------------------------------------------------
   2047     // Methods called by activity
   2048     //-------------------------------------------------------------------------
   2049 
   2050     /**
   2051      * See {@link WebView#savePassword(String, String, String)}
   2052      */
   2053     @Override
   2054     public void savePassword(String host, String username, String password) {
   2055         mDatabase.setUsernamePassword(host, username, password);
   2056     }
   2057 
   2058     /**
   2059      * See {@link WebView#setHttpAuthUsernamePassword(String, String, String, String)}
   2060      */
   2061     @Override
   2062     public void setHttpAuthUsernamePassword(String host, String realm,
   2063             String username, String password) {
   2064         mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
   2065     }
   2066 
   2067     /**
   2068      * See {@link WebView#getHttpAuthUsernamePassword(String, String)}
   2069      */
   2070     @Override
   2071     public String[] getHttpAuthUsernamePassword(String host, String realm) {
   2072         return mDatabase.getHttpAuthUsernamePassword(host, realm);
   2073     }
   2074 
   2075     /**
   2076      * Remove Find or Select ActionModes, if active.
   2077      */
   2078     private void clearActionModes() {
   2079         if (mSelectCallback != null) {
   2080             mSelectCallback.finish();
   2081         }
   2082         if (mFindCallback != null) {
   2083             mFindCallback.finish();
   2084         }
   2085     }
   2086 
   2087     /**
   2088      * Called to clear state when moving from one page to another, or changing
   2089      * in some other way that makes elements associated with the current page
   2090      * (such as ActionModes) no longer relevant.
   2091      */
   2092     private void clearHelpers() {
   2093         hideSoftKeyboard();
   2094         clearActionModes();
   2095         dismissFullScreenMode();
   2096         cancelDialogs();
   2097     }
   2098 
   2099     private void cancelDialogs() {
   2100         if (mListBoxDialog != null) {
   2101             mListBoxDialog.cancel();
   2102             mListBoxDialog = null;
   2103         }
   2104         if (mSavePasswordDialog != null) {
   2105             mSavePasswordDialog.dismiss();
   2106             mSavePasswordDialog = null;
   2107         }
   2108     }
   2109 
   2110     /**
   2111      * See {@link WebView#destroy()}
   2112      */
   2113     @Override
   2114     public void destroy() {
   2115         if (mWebView.getViewRootImpl() != null) {
   2116             Log.e(LOGTAG, "Error: WebView.destroy() called while still attached!");
   2117         }
   2118         ensureFunctorDetached();
   2119         destroyJava();
   2120         destroyNative();
   2121     }
   2122 
   2123     private void ensureFunctorDetached() {
   2124         if (mWebView.isHardwareAccelerated()) {
   2125             int drawGLFunction = nativeGetDrawGLFunction(mNativeClass);
   2126             ViewRootImpl viewRoot = mWebView.getViewRootImpl();
   2127             if (drawGLFunction != 0 && viewRoot != null) {
   2128                 viewRoot.detachFunctor(drawGLFunction);
   2129             }
   2130         }
   2131     }
   2132 
   2133     private void destroyJava() {
   2134         mCallbackProxy.blockMessages();
   2135         if (mAccessibilityInjector != null) {
   2136             mAccessibilityInjector.destroy();
   2137             mAccessibilityInjector = null;
   2138         }
   2139         if (mWebViewCore != null) {
   2140             // Tell WebViewCore to destroy itself
   2141             synchronized (this) {
   2142                 WebViewCore webViewCore = mWebViewCore;
   2143                 mWebViewCore = null; // prevent using partial webViewCore
   2144                 webViewCore.destroy();
   2145             }
   2146             // Remove any pending messages that might not be serviced yet.
   2147             mPrivateHandler.removeCallbacksAndMessages(null);
   2148         }
   2149     }
   2150 
   2151     private void destroyNative() {
   2152         if (mNativeClass == 0) return;
   2153         int nptr = mNativeClass;
   2154         mNativeClass = 0;
   2155         if (Thread.currentThread() == mPrivateHandler.getLooper().getThread()) {
   2156             // We are on the main thread and can safely delete
   2157             nativeDestroy(nptr);
   2158         } else {
   2159             mPrivateHandler.post(new DestroyNativeRunnable(nptr));
   2160         }
   2161     }
   2162 
   2163     private static class DestroyNativeRunnable implements Runnable {
   2164 
   2165         private int mNativePtr;
   2166 
   2167         public DestroyNativeRunnable(int nativePtr) {
   2168             mNativePtr = nativePtr;
   2169         }
   2170 
   2171         @Override
   2172         public void run() {
   2173             // nativeDestroy also does a stopGL()
   2174             nativeDestroy(mNativePtr);
   2175         }
   2176 
   2177     }
   2178 
   2179     /**
   2180      * See {@link WebView#enablePlatformNotifications()}
   2181      */
   2182     @Deprecated
   2183     public static void enablePlatformNotifications() {
   2184         synchronized (WebViewClassic.class) {
   2185             sNotificationsEnabled = true;
   2186             Context context = JniUtil.getContext();
   2187             if (context != null)
   2188                 setupProxyListener(context);
   2189         }
   2190     }
   2191 
   2192     /**
   2193      * See {@link WebView#disablePlatformNotifications()}
   2194      */
   2195     @Deprecated
   2196     public static void disablePlatformNotifications() {
   2197         synchronized (WebViewClassic.class) {
   2198             sNotificationsEnabled = false;
   2199             Context context = JniUtil.getContext();
   2200             if (context != null)
   2201                 disableProxyListener(context);
   2202         }
   2203     }
   2204 
   2205     /**
   2206      * Sets JavaScript engine flags.
   2207      *
   2208      * @param flags JS engine flags in a String
   2209      *
   2210      * This is an implementation detail.
   2211      */
   2212     public void setJsFlags(String flags) {
   2213         mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
   2214     }
   2215 
   2216     /**
   2217      * See {@link WebView#setNetworkAvailable(boolean)}
   2218      */
   2219     @Override
   2220     public void setNetworkAvailable(boolean networkUp) {
   2221         mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
   2222                 networkUp ? 1 : 0, 0);
   2223     }
   2224 
   2225     /**
   2226      * Inform WebView about the current network type.
   2227      */
   2228     public void setNetworkType(String type, String subtype) {
   2229         Map<String, String> map = new HashMap<String, String>();
   2230         map.put("type", type);
   2231         map.put("subtype", subtype);
   2232         mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
   2233     }
   2234 
   2235     /**
   2236      * See {@link WebView#saveState(Bundle)}
   2237      */
   2238     @Override
   2239     public WebBackForwardList saveState(Bundle outState) {
   2240         if (outState == null) {
   2241             return null;
   2242         }
   2243         // We grab a copy of the back/forward list because a client of WebView
   2244         // may have invalidated the history list by calling clearHistory.
   2245         WebBackForwardListClassic list = copyBackForwardList();
   2246         final int currentIndex = list.getCurrentIndex();
   2247         final int size = list.getSize();
   2248         // We should fail saving the state if the list is empty or the index is
   2249         // not in a valid range.
   2250         if (currentIndex < 0 || currentIndex >= size || size == 0) {
   2251             return null;
   2252         }
   2253         outState.putInt("index", currentIndex);
   2254         // FIXME: This should just be a byte[][] instead of ArrayList but
   2255         // Parcel.java does not have the code to handle multi-dimensional
   2256         // arrays.
   2257         ArrayList<byte[]> history = new ArrayList<byte[]>(size);
   2258         for (int i = 0; i < size; i++) {
   2259             WebHistoryItemClassic item = list.getItemAtIndex(i);
   2260             if (null == item) {
   2261                 // FIXME: this shouldn't happen
   2262                 // need to determine how item got set to null
   2263                 Log.w(LOGTAG, "saveState: Unexpected null history item.");
   2264                 return null;
   2265             }
   2266             byte[] data = item.getFlattenedData();
   2267             if (data == null) {
   2268                 // It would be very odd to not have any data for a given history
   2269                 // item. And we will fail to rebuild the history list without
   2270                 // flattened data.
   2271                 return null;
   2272             }
   2273             history.add(data);
   2274         }
   2275         outState.putSerializable("history", history);
   2276         if (mCertificate != null) {
   2277             outState.putBundle("certificate",
   2278                                SslCertificate.saveState(mCertificate));
   2279         }
   2280         outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
   2281         mZoomManager.saveZoomState(outState);
   2282         return list;
   2283     }
   2284 
   2285     /**
   2286      * See {@link WebView#savePicture(Bundle, File)}
   2287      */
   2288     @Override
   2289     @Deprecated
   2290     public boolean savePicture(Bundle b, final File dest) {
   2291         if (dest == null || b == null) {
   2292             return false;
   2293         }
   2294         final Picture p = capturePicture();
   2295         // Use a temporary file while writing to ensure the destination file
   2296         // contains valid data.
   2297         final File temp = new File(dest.getPath() + ".writing");
   2298         new Thread(new Runnable() {
   2299             @Override
   2300             public void run() {
   2301                 FileOutputStream out = null;
   2302                 try {
   2303                     out = new FileOutputStream(temp);
   2304                     p.writeToStream(out);
   2305                     // Writing the picture succeeded, rename the temporary file
   2306                     // to the destination.
   2307                     temp.renameTo(dest);
   2308                 } catch (Exception e) {
   2309                     // too late to do anything about it.
   2310                 } finally {
   2311                     if (out != null) {
   2312                         try {
   2313                             out.close();
   2314                         } catch (Exception e) {
   2315                             // Can't do anything about that
   2316                         }
   2317                     }
   2318                     temp.delete();
   2319                 }
   2320             }
   2321         }).start();
   2322         // now update the bundle
   2323         b.putInt("scrollX", getScrollX());
   2324         b.putInt("scrollY", getScrollY());
   2325         mZoomManager.saveZoomState(b);
   2326         return true;
   2327     }
   2328 
   2329     private void restoreHistoryPictureFields(Picture p, Bundle b) {
   2330         int sx = b.getInt("scrollX", 0);
   2331         int sy = b.getInt("scrollY", 0);
   2332 
   2333         mDrawHistory = true;
   2334         mHistoryPicture = p;
   2335 
   2336         setScrollXRaw(sx);
   2337         setScrollYRaw(sy);
   2338         mZoomManager.restoreZoomState(b);
   2339         final float scale = mZoomManager.getScale();
   2340         mHistoryWidth = Math.round(p.getWidth() * scale);
   2341         mHistoryHeight = Math.round(p.getHeight() * scale);
   2342 
   2343         invalidate();
   2344     }
   2345 
   2346     /**
   2347      * See {@link WebView#restorePicture(Bundle, File)};
   2348      */
   2349     @Override
   2350     @Deprecated
   2351     public boolean restorePicture(Bundle b, File src) {
   2352         if (src == null || b == null) {
   2353             return false;
   2354         }
   2355         if (!src.exists()) {
   2356             return false;
   2357         }
   2358         try {
   2359             final FileInputStream in = new FileInputStream(src);
   2360             final Bundle copy = new Bundle(b);
   2361             new Thread(new Runnable() {
   2362                 @Override
   2363                 public void run() {
   2364                     try {
   2365                         final Picture p = Picture.createFromStream(in);
   2366                         if (p != null) {
   2367                             // Post a runnable on the main thread to update the
   2368                             // history picture fields.
   2369                             mPrivateHandler.post(new Runnable() {
   2370                                 @Override
   2371                                 public void run() {
   2372                                     restoreHistoryPictureFields(p, copy);
   2373                                 }
   2374                             });
   2375                         }
   2376                     } finally {
   2377                         try {
   2378                             in.close();
   2379                         } catch (Exception e) {
   2380                             // Nothing we can do now.
   2381                         }
   2382                     }
   2383                 }
   2384             }).start();
   2385         } catch (FileNotFoundException e){
   2386             e.printStackTrace();
   2387         }
   2388         return true;
   2389     }
   2390 
   2391     /**
   2392      * Saves the view data to the output stream. The output is highly
   2393      * version specific, and may not be able to be loaded by newer versions
   2394      * of WebView.
   2395      * @param stream The {@link OutputStream} to save to
   2396      * @param callback The {@link ValueCallback} to call with the result
   2397      */
   2398     public void saveViewState(OutputStream stream, ValueCallback<Boolean> callback) {
   2399         if (mWebViewCore == null) {
   2400             callback.onReceiveValue(false);
   2401             return;
   2402         }
   2403         mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SAVE_VIEW_STATE,
   2404                 new WebViewCore.SaveViewStateRequest(stream, callback));
   2405     }
   2406 
   2407     /**
   2408      * Loads the view data from the input stream. See
   2409      * {@link #saveViewState(java.io.OutputStream, ValueCallback)} for more information.
   2410      * @param stream The {@link InputStream} to load from
   2411      */
   2412     public void loadViewState(InputStream stream) {
   2413         mBlockWebkitViewMessages = true;
   2414         new AsyncTask<InputStream, Void, DrawData>() {
   2415 
   2416             @Override
   2417             protected DrawData doInBackground(InputStream... params) {
   2418                 try {
   2419                     return ViewStateSerializer.deserializeViewState(params[0]);
   2420                 } catch (IOException e) {
   2421                     return null;
   2422                 }
   2423             }
   2424 
   2425             @Override
   2426             protected void onPostExecute(DrawData draw) {
   2427                 if (draw == null) {
   2428                     Log.e(LOGTAG, "Failed to load view state!");
   2429                     return;
   2430                 }
   2431                 int viewWidth = getViewWidth();
   2432                 int viewHeight = getViewHeightWithTitle() - getTitleHeight();
   2433                 draw.mViewSize = new Point(viewWidth, viewHeight);
   2434                 draw.mViewState.mDefaultScale = getDefaultZoomScale();
   2435                 mLoadedPicture = draw;
   2436                 setNewPicture(mLoadedPicture, true);
   2437                 mLoadedPicture.mViewState = null;
   2438             }
   2439 
   2440         }.execute(stream);
   2441     }
   2442 
   2443     /**
   2444      * Clears the view state set with {@link #loadViewState(InputStream)}.
   2445      * This WebView will then switch to showing the content from webkit
   2446      */
   2447     public void clearViewState() {
   2448         mBlockWebkitViewMessages = false;
   2449         mLoadedPicture = null;
   2450         invalidate();
   2451     }
   2452 
   2453     /**
   2454      * See {@link WebView#restoreState(Bundle)}
   2455      */
   2456     @Override
   2457     public WebBackForwardList restoreState(Bundle inState) {
   2458         WebBackForwardListClassic returnList = null;
   2459         if (inState == null) {
   2460             return returnList;
   2461         }
   2462         if (inState.containsKey("index") && inState.containsKey("history")) {
   2463             mCertificate = SslCertificate.restoreState(
   2464                 inState.getBundle("certificate"));
   2465 
   2466             final WebBackForwardListClassic list = mCallbackProxy.getBackForwardList();
   2467             final int index = inState.getInt("index");
   2468             // We can't use a clone of the list because we need to modify the
   2469             // shared copy, so synchronize instead to prevent concurrent
   2470             // modifications.
   2471             synchronized (list) {
   2472                 final List<byte[]> history =
   2473                         (List<byte[]>) inState.getSerializable("history");
   2474                 final int size = history.size();
   2475                 // Check the index bounds so we don't crash in native code while
   2476                 // restoring the history index.
   2477                 if (index < 0 || index >= size) {
   2478                     return null;
   2479                 }
   2480                 for (int i = 0; i < size; i++) {
   2481                     byte[] data = history.remove(0);
   2482                     if (data == null) {
   2483                         // If we somehow have null data, we cannot reconstruct
   2484                         // the item and thus our history list cannot be rebuilt.
   2485                         return null;
   2486                     }
   2487                     WebHistoryItem item = new WebHistoryItemClassic(data);
   2488                     list.addHistoryItem(item);
   2489                 }
   2490                 // Grab the most recent copy to return to the caller.
   2491                 returnList = copyBackForwardList();
   2492                 // Update the copy to have the correct index.
   2493                 returnList.setCurrentIndex(index);
   2494             }
   2495             // Restore private browsing setting.
   2496             if (inState.getBoolean("privateBrowsingEnabled")) {
   2497                 getSettings().setPrivateBrowsingEnabled(true);
   2498             }
   2499             mZoomManager.restoreZoomState(inState);
   2500             // Remove all pending messages because we are restoring previous
   2501             // state.
   2502             mWebViewCore.removeMessages();
   2503             if (isAccessibilityInjectionEnabled()) {
   2504                 getAccessibilityInjector().addAccessibilityApisIfNecessary();
   2505             }
   2506             // Send a restore state message.
   2507             mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
   2508         }
   2509         return returnList;
   2510     }
   2511 
   2512     /**
   2513      * See {@link WebView#loadUrl(String, Map)}
   2514      */
   2515     @Override
   2516     public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
   2517         loadUrlImpl(url, additionalHttpHeaders);
   2518     }
   2519 
   2520     private void loadUrlImpl(String url, Map<String, String> extraHeaders) {
   2521         switchOutDrawHistory();
   2522         WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
   2523         arg.mUrl = url;
   2524         arg.mExtraHeaders = extraHeaders;
   2525         mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
   2526         clearHelpers();
   2527     }
   2528 
   2529     /**
   2530      * See {@link WebView#loadUrl(String)}
   2531      */
   2532     @Override
   2533     public void loadUrl(String url) {
   2534         loadUrlImpl(url);
   2535     }
   2536 
   2537     private void loadUrlImpl(String url) {
   2538         if (url == null) {
   2539             return;
   2540         }
   2541         loadUrlImpl(url, null);
   2542     }
   2543 
   2544     /**
   2545      * See {@link WebView#postUrl(String, byte[])}
   2546      */
   2547     @Override
   2548     public void postUrl(String url, byte[] postData) {
   2549         if (URLUtil.isNetworkUrl(url)) {
   2550             switchOutDrawHistory();
   2551             WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
   2552             arg.mUrl = url;
   2553             arg.mPostData = postData;
   2554             mWebViewCore.sendMessage(EventHub.POST_URL, arg);
   2555             clearHelpers();
   2556         } else {
   2557             loadUrlImpl(url);
   2558         }
   2559     }
   2560 
   2561     /**
   2562      * See {@link WebView#loadData(String, String, String)}
   2563      */
   2564     @Override
   2565     public void loadData(String data, String mimeType, String encoding) {
   2566         loadDataImpl(data, mimeType, encoding);
   2567     }
   2568 
   2569     private void loadDataImpl(String data, String mimeType, String encoding) {
   2570         StringBuilder dataUrl = new StringBuilder("data:");
   2571         dataUrl.append(mimeType);
   2572         if ("base64".equals(encoding)) {
   2573             dataUrl.append(";base64");
   2574         }
   2575         dataUrl.append(",");
   2576         dataUrl.append(data);
   2577         loadUrlImpl(dataUrl.toString());
   2578     }
   2579 
   2580     /**
   2581      * See {@link WebView#loadDataWithBaseURL(String, String, String, String, String)}
   2582      */
   2583     @Override
   2584     public void loadDataWithBaseURL(String baseUrl, String data,
   2585             String mimeType, String encoding, String historyUrl) {
   2586 
   2587         if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
   2588             loadDataImpl(data, mimeType, encoding);
   2589             return;
   2590         }
   2591         switchOutDrawHistory();
   2592         WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
   2593         arg.mBaseUrl = baseUrl;
   2594         arg.mData = data;
   2595         arg.mMimeType = mimeType;
   2596         arg.mEncoding = encoding;
   2597         arg.mHistoryUrl = historyUrl;
   2598         mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
   2599         clearHelpers();
   2600     }
   2601 
   2602     /**
   2603      * See {@link WebView#saveWebArchive(String)}
   2604      */
   2605     @Override
   2606     public void saveWebArchive(String filename) {
   2607         saveWebArchiveImpl(filename, false, null);
   2608     }
   2609 
   2610     /* package */ static class SaveWebArchiveMessage {
   2611         SaveWebArchiveMessage (String basename, boolean autoname, ValueCallback<String> callback) {
   2612             mBasename = basename;
   2613             mAutoname = autoname;
   2614             mCallback = callback;
   2615         }
   2616 
   2617         /* package */ final String mBasename;
   2618         /* package */ final boolean mAutoname;
   2619         /* package */ final ValueCallback<String> mCallback;
   2620         /* package */ String mResultFile;
   2621     }
   2622 
   2623     /**
   2624      * See {@link WebView#saveWebArchive(String, boolean, ValueCallback)}
   2625      */
   2626     @Override
   2627     public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
   2628         saveWebArchiveImpl(basename, autoname, callback);
   2629     }
   2630 
   2631     private void saveWebArchiveImpl(String basename, boolean autoname,
   2632             ValueCallback<String> callback) {
   2633         mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
   2634             new SaveWebArchiveMessage(basename, autoname, callback));
   2635     }
   2636 
   2637     /**
   2638      * See {@link WebView#stopLoading()}
   2639      */
   2640     @Override
   2641     public void stopLoading() {
   2642         // TODO: should we clear all the messages in the queue before sending
   2643         // STOP_LOADING?
   2644         switchOutDrawHistory();
   2645         mWebViewCore.sendMessage(EventHub.STOP_LOADING);
   2646     }
   2647 
   2648     /**
   2649      * See {@link WebView#reload()}
   2650      */
   2651     @Override
   2652     public void reload() {
   2653         clearHelpers();
   2654         switchOutDrawHistory();
   2655         mWebViewCore.sendMessage(EventHub.RELOAD);
   2656     }
   2657 
   2658     /**
   2659      * See {@link WebView#canGoBack()}
   2660      */
   2661     @Override
   2662     public boolean canGoBack() {
   2663         WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
   2664         synchronized (l) {
   2665             if (l.getClearPending()) {
   2666                 return false;
   2667             } else {
   2668                 return l.getCurrentIndex() > 0;
   2669             }
   2670         }
   2671     }
   2672 
   2673     /**
   2674      * See {@link WebView#goBack()}
   2675      */
   2676     @Override
   2677     public void goBack() {
   2678         goBackOrForwardImpl(-1);
   2679     }
   2680 
   2681     /**
   2682      * See {@link WebView#canGoForward()}
   2683      */
   2684     @Override
   2685     public boolean canGoForward() {
   2686         WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
   2687         synchronized (l) {
   2688             if (l.getClearPending()) {
   2689                 return false;
   2690             } else {
   2691                 return l.getCurrentIndex() < l.getSize() - 1;
   2692             }
   2693         }
   2694     }
   2695 
   2696     /**
   2697      * See {@link WebView#goForward()}
   2698      */
   2699     @Override
   2700     public void goForward() {
   2701         goBackOrForwardImpl(1);
   2702     }
   2703 
   2704     /**
   2705      * See {@link WebView#canGoBackOrForward(int)}
   2706      */
   2707     @Override
   2708     public boolean canGoBackOrForward(int steps) {
   2709         WebBackForwardListClassic l = mCallbackProxy.getBackForwardList();
   2710         synchronized (l) {
   2711             if (l.getClearPending()) {
   2712                 return false;
   2713             } else {
   2714                 int newIndex = l.getCurrentIndex() + steps;
   2715                 return newIndex >= 0 && newIndex < l.getSize();
   2716             }
   2717         }
   2718     }
   2719 
   2720     /**
   2721      * See {@link WebView#goBackOrForward(int)}
   2722      */
   2723     @Override
   2724     public void goBackOrForward(int steps) {
   2725         goBackOrForwardImpl(steps);
   2726     }
   2727 
   2728     private void goBackOrForwardImpl(int steps) {
   2729         goBackOrForward(steps, false);
   2730     }
   2731 
   2732     private void goBackOrForward(int steps, boolean ignoreSnapshot) {
   2733         if (steps != 0) {
   2734             clearHelpers();
   2735             mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
   2736                     ignoreSnapshot ? 1 : 0);
   2737         }
   2738     }
   2739 
   2740     /**
   2741      * See {@link WebView#isPrivateBrowsingEnabled()}
   2742      */
   2743     @Override
   2744     public boolean isPrivateBrowsingEnabled() {
   2745         WebSettingsClassic settings = getSettings();
   2746         return (settings != null) ? settings.isPrivateBrowsingEnabled() : false;
   2747     }
   2748 
   2749     private void startPrivateBrowsing() {
   2750         getSettings().setPrivateBrowsingEnabled(true);
   2751     }
   2752 
   2753     private boolean extendScroll(int y) {
   2754         int finalY = mScroller.getFinalY();
   2755         int newY = pinLocY(finalY + y);
   2756         if (newY == finalY) return false;
   2757         mScroller.setFinalY(newY);
   2758         mScroller.extendDuration(computeDuration(0, y));
   2759         return true;
   2760     }
   2761 
   2762     /**
   2763      * See {@link WebView#pageUp(boolean)}
   2764      */
   2765     @Override
   2766     public boolean pageUp(boolean top) {
   2767         if (mNativeClass == 0) {
   2768             return false;
   2769         }
   2770         if (top) {
   2771             // go to the top of the document
   2772             return pinScrollTo(getScrollX(), 0, true, 0);
   2773         }
   2774         // Page up
   2775         int h = getHeight();
   2776         int y;
   2777         if (h > 2 * PAGE_SCROLL_OVERLAP) {
   2778             y = -h + PAGE_SCROLL_OVERLAP;
   2779         } else {
   2780             y = -h / 2;
   2781         }
   2782         return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
   2783                 : extendScroll(y);
   2784     }
   2785 
   2786     /**
   2787      * See {@link WebView#pageDown(boolean)}
   2788      */
   2789     @Override
   2790     public boolean pageDown(boolean bottom) {
   2791         if (mNativeClass == 0) {
   2792             return false;
   2793         }
   2794         if (bottom) {
   2795             return pinScrollTo(getScrollX(), computeRealVerticalScrollRange(), true, 0);
   2796         }
   2797         // Page down.
   2798         int h = getHeight();
   2799         int y;
   2800         if (h > 2 * PAGE_SCROLL_OVERLAP) {
   2801             y = h - PAGE_SCROLL_OVERLAP;
   2802         } else {
   2803             y = h / 2;
   2804         }
   2805         return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
   2806                 : extendScroll(y);
   2807     }
   2808 
   2809     /**
   2810      * See {@link WebView#clearView()}
   2811      */
   2812     @Override
   2813     public void clearView() {
   2814         mContentWidth = 0;
   2815         mContentHeight = 0;
   2816         setBaseLayer(0, false, false);
   2817         mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
   2818     }
   2819 
   2820     /**
   2821      * See {@link WebView#capturePicture()}
   2822      */
   2823     @Override
   2824     public Picture capturePicture() {
   2825         if (mNativeClass == 0) return null;
   2826         Picture result = new Picture();
   2827         nativeCopyBaseContentToPicture(result);
   2828         return result;
   2829     }
   2830 
   2831     /**
   2832      * See {@link WebView#getScale()}
   2833      */
   2834     @Override
   2835     public float getScale() {
   2836         return mZoomManager.getScale();
   2837     }
   2838 
   2839     /**
   2840      * Compute the reading level scale of the WebView
   2841      * @param scale The current scale.
   2842      * @return The reading level scale.
   2843      */
   2844     /*package*/ float computeReadingLevelScale(float scale) {
   2845         return mZoomManager.computeReadingLevelScale(scale);
   2846     }
   2847 
   2848     /**
   2849      * See {@link WebView#setInitialScale(int)}
   2850      */
   2851     @Override
   2852     public void setInitialScale(int scaleInPercent) {
   2853         mZoomManager.setInitialScaleInPercent(scaleInPercent);
   2854     }
   2855 
   2856     /**
   2857      * See {@link WebView#invokeZoomPicker()}
   2858      */
   2859     @Override
   2860     public void invokeZoomPicker() {
   2861         if (!getSettings().supportZoom()) {
   2862             Log.w(LOGTAG, "This WebView doesn't support zoom.");
   2863             return;
   2864         }
   2865         clearHelpers();
   2866         mZoomManager.invokeZoomPicker();
   2867     }
   2868 
   2869     /**
   2870      * See {@link WebView#getHitTestResult()}
   2871      */
   2872     @Override
   2873     public HitTestResult getHitTestResult() {
   2874         return mInitialHitTestResult;
   2875     }
   2876 
   2877     // No left edge for double-tap zoom alignment
   2878     static final int NO_LEFTEDGE = -1;
   2879 
   2880     int getBlockLeftEdge(int x, int y, float readingScale) {
   2881         float invReadingScale = 1.0f / readingScale;
   2882         int readingWidth = (int) (getViewWidth() * invReadingScale);
   2883         int left = NO_LEFTEDGE;
   2884         if (mFocusedNode != null) {
   2885             final int length = mFocusedNode.mEnclosingParentRects.length;
   2886             for (int i = 0; i < length; i++) {
   2887                 Rect rect = mFocusedNode.mEnclosingParentRects[i];
   2888                 if (rect.width() < mFocusedNode.mHitTestSlop) {
   2889                     // ignore bounding boxes that are too small
   2890                     continue;
   2891                 } else if (rect.width() > readingWidth) {
   2892                     // stop when bounding box doesn't fit the screen width
   2893                     // at reading scale
   2894                     break;
   2895                 }
   2896 
   2897                 left = rect.left;
   2898             }
   2899         }
   2900 
   2901         return left;
   2902     }
   2903 
   2904     /**
   2905      * See {@link WebView#requestFocusNodeHref(Message)}
   2906      */
   2907     @Override
   2908     public void requestFocusNodeHref(Message hrefMsg) {
   2909         if (hrefMsg == null) {
   2910             return;
   2911         }
   2912         int contentX = viewToContentX(mLastTouchX + getScrollX());
   2913         int contentY = viewToContentY(mLastTouchY + getScrollY());
   2914         if (mFocusedNode != null && mFocusedNode.mHitTestX == contentX
   2915                 && mFocusedNode.mHitTestY == contentY) {
   2916             hrefMsg.getData().putString(FocusNodeHref.URL, mFocusedNode.mLinkUrl);
   2917             hrefMsg.getData().putString(FocusNodeHref.TITLE, mFocusedNode.mAnchorText);
   2918             hrefMsg.getData().putString(FocusNodeHref.SRC, mFocusedNode.mImageUrl);
   2919             hrefMsg.sendToTarget();
   2920             return;
   2921         }
   2922         mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
   2923                 contentX, contentY, hrefMsg);
   2924     }
   2925 
   2926     /**
   2927      * See {@link WebView#requestImageRef(Message)}
   2928      */
   2929     @Override
   2930     public void requestImageRef(Message msg) {
   2931         if (0 == mNativeClass) return; // client isn't initialized
   2932         String url = mFocusedNode != null ? mFocusedNode.mImageUrl : null;
   2933         Bundle data = msg.getData();
   2934         data.putString("url", url);
   2935         msg.setData(data);
   2936         msg.sendToTarget();
   2937     }
   2938 
   2939     static int pinLoc(int x, int viewMax, int docMax) {
   2940 //        Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
   2941         if (docMax < viewMax) {   // the doc has room on the sides for "blank"
   2942             // pin the short document to the top/left of the screen
   2943             x = 0;
   2944 //            Log.d(LOGTAG, "--- center " + x);
   2945         } else if (x < 0) {
   2946             x = 0;
   2947 //            Log.d(LOGTAG, "--- zero");
   2948         } else if (x + viewMax > docMax) {
   2949             x = docMax - viewMax;
   2950 //            Log.d(LOGTAG, "--- pin " + x);
   2951         }
   2952         return x;
   2953     }
   2954 
   2955     // Expects x in view coordinates
   2956     int pinLocX(int x) {
   2957         if (mInOverScrollMode) return x;
   2958         return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
   2959     }
   2960 
   2961     // Expects y in view coordinates
   2962     int pinLocY(int y) {
   2963         if (mInOverScrollMode) return y;
   2964         return pinLoc(y, getViewHeightWithTitle(),
   2965                       computeRealVerticalScrollRange() + getTitleHeight());
   2966     }
   2967 
   2968     /**
   2969      * Given a distance in view space, convert it to content space. Note: this
   2970      * does not reflect translation, just scaling, so this should not be called
   2971      * with coordinates, but should be called for dimensions like width or
   2972      * height.
   2973      */
   2974     private int viewToContentDimension(int d) {
   2975         return Math.round(d * mZoomManager.getInvScale());
   2976     }
   2977 
   2978     /**
   2979      * Given an x coordinate in view space, convert it to content space.  Also
   2980      * may be used for absolute heights.
   2981      */
   2982     /*package*/ int viewToContentX(int x) {
   2983         return viewToContentDimension(x);
   2984     }
   2985 
   2986     /**
   2987      * Given a y coordinate in view space, convert it to content space.
   2988      * Takes into account the height of the title bar if there is one
   2989      * embedded into the WebView.
   2990      */
   2991     /*package*/ int viewToContentY(int y) {
   2992         return viewToContentDimension(y - getTitleHeight());
   2993     }
   2994 
   2995     /**
   2996      * Given a x coordinate in view space, convert it to content space.
   2997      * Returns the result as a float.
   2998      */
   2999     private float viewToContentXf(int x) {
   3000         return x * mZoomManager.getInvScale();
   3001     }
   3002 
   3003     /**
   3004      * Given a y coordinate in view space, convert it to content space.
   3005      * Takes into account the height of the title bar if there is one
   3006      * embedded into the WebView. Returns the result as a float.
   3007      */
   3008     private float viewToContentYf(int y) {
   3009         return (y - getTitleHeight()) * mZoomManager.getInvScale();
   3010     }
   3011 
   3012     /**
   3013      * Given a distance in content space, convert it to view space. Note: this
   3014      * does not reflect translation, just scaling, so this should not be called
   3015      * with coordinates, but should be called for dimensions like width or
   3016      * height.
   3017      */
   3018     /*package*/ int contentToViewDimension(int d) {
   3019         return Math.round(d * mZoomManager.getScale());
   3020     }
   3021 
   3022     /**
   3023      * Given an x coordinate in content space, convert it to view
   3024      * space.
   3025      */
   3026     /*package*/ int contentToViewX(int x) {
   3027         return contentToViewDimension(x);
   3028     }
   3029 
   3030     /**
   3031      * Given a y coordinate in content space, convert it to view
   3032      * space.  Takes into account the height of the title bar.
   3033      */
   3034     /*package*/ int contentToViewY(int y) {
   3035         return contentToViewDimension(y) + getTitleHeight();
   3036     }
   3037 
   3038     private Rect contentToViewRect(Rect x) {
   3039         return new Rect(contentToViewX(x.left), contentToViewY(x.top),
   3040                         contentToViewX(x.right), contentToViewY(x.bottom));
   3041     }
   3042 
   3043     /*  To invalidate a rectangle in content coordinates, we need to transform
   3044         the rect into view coordinates, so we can then call invalidate(...).
   3045 
   3046         Normally, we would just call contentToView[XY](...), which eventually
   3047         calls Math.round(coordinate * mActualScale). However, for invalidates,
   3048         we need to account for the slop that occurs with antialiasing. To
   3049         address that, we are a little more liberal in the size of the rect that
   3050         we invalidate.
   3051 
   3052         This liberal calculation calls floor() for the top/left, and ceil() for
   3053         the bottom/right coordinates. This catches the possible extra pixels of
   3054         antialiasing that we might have missed with just round().
   3055      */
   3056 
   3057     // Called by JNI to invalidate the View, given rectangle coordinates in
   3058     // content space
   3059     private void viewInvalidate(int l, int t, int r, int b) {
   3060         final float scale = mZoomManager.getScale();
   3061         final int dy = getTitleHeight();
   3062         mWebView.invalidate((int)Math.floor(l * scale),
   3063                 (int)Math.floor(t * scale) + dy,
   3064                 (int)Math.ceil(r * scale),
   3065                 (int)Math.ceil(b * scale) + dy);
   3066     }
   3067 
   3068     // Called by JNI to invalidate the View after a delay, given rectangle
   3069     // coordinates in content space
   3070     private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
   3071         final float scale = mZoomManager.getScale();
   3072         final int dy = getTitleHeight();
   3073         mWebView.postInvalidateDelayed(delay,
   3074                 (int)Math.floor(l * scale),
   3075                 (int)Math.floor(t * scale) + dy,
   3076                 (int)Math.ceil(r * scale),
   3077                 (int)Math.ceil(b * scale) + dy);
   3078     }
   3079 
   3080     private void invalidateContentRect(Rect r) {
   3081         viewInvalidate(r.left, r.top, r.right, r.bottom);
   3082     }
   3083 
   3084     // stop the scroll animation, and don't let a subsequent fling add
   3085     // to the existing velocity
   3086     private void abortAnimation() {
   3087         mScroller.abortAnimation();
   3088         mLastVelocity = 0;
   3089     }
   3090 
   3091     /* call from webcoreview.draw(), so we're still executing in the UI thread
   3092     */
   3093     private void recordNewContentSize(int w, int h, boolean updateLayout) {
   3094 
   3095         // premature data from webkit, ignore
   3096         if ((w | h) == 0) {
   3097             invalidate();
   3098             return;
   3099         }
   3100 
   3101         // don't abort a scroll animation if we didn't change anything
   3102         if (mContentWidth != w || mContentHeight != h) {
   3103             // record new dimensions
   3104             mContentWidth = w;
   3105             mContentHeight = h;
   3106             // If history Picture is drawn, don't update scroll. They will be
   3107             // updated when we get out of that mode.
   3108             if (!mDrawHistory) {
   3109                 // repin our scroll, taking into account the new content size
   3110                 updateScrollCoordinates(pinLocX(getScrollX()), pinLocY(getScrollY()));
   3111                 if (!mScroller.isFinished()) {
   3112                     // We are in the middle of a scroll.  Repin the final scroll
   3113                     // position.
   3114                     mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
   3115                     mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
   3116                 }
   3117             }
   3118             invalidate();
   3119         }
   3120         contentSizeChanged(updateLayout);
   3121     }
   3122 
   3123     // Used to avoid sending many visible rect messages.
   3124     private Rect mLastVisibleRectSent = new Rect();
   3125     private Rect mLastGlobalRect = new Rect();
   3126     private Rect mVisibleRect = new Rect();
   3127     private Rect mGlobalVisibleRect = new Rect();
   3128     private Point mScrollOffset = new Point();
   3129 
   3130     Rect sendOurVisibleRect() {
   3131         if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent;
   3132         calcOurContentVisibleRect(mVisibleRect);
   3133         // Rect.equals() checks for null input.
   3134         if (!mVisibleRect.equals(mLastVisibleRectSent)) {
   3135             if (!mBlockWebkitViewMessages) {
   3136                 mScrollOffset.set(mVisibleRect.left, mVisibleRect.top);
   3137                 mWebViewCore.removeMessages(EventHub.SET_SCROLL_OFFSET);
   3138                 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
   3139                         mSendScrollEvent ? 1 : 0, mScrollOffset);
   3140             }
   3141             mLastVisibleRectSent.set(mVisibleRect);
   3142             mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
   3143         }
   3144         if (mWebView.getGlobalVisibleRect(mGlobalVisibleRect)
   3145                 && !mGlobalVisibleRect.equals(mLastGlobalRect)) {
   3146             if (DebugFlags.WEB_VIEW) {
   3147                 Log.v(LOGTAG, "sendOurVisibleRect=(" + mGlobalVisibleRect.left + ","
   3148                         + mGlobalVisibleRect.top + ",r=" + mGlobalVisibleRect.right + ",b="
   3149                         + mGlobalVisibleRect.bottom);
   3150             }
   3151             // TODO: the global offset is only used by windowRect()
   3152             // in ChromeClientAndroid ; other clients such as touch
   3153             // and mouse events could return view + screen relative points.
   3154             if (!mBlockWebkitViewMessages) {
   3155                 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, mGlobalVisibleRect);
   3156             }
   3157             mLastGlobalRect.set(mGlobalVisibleRect);
   3158         }
   3159         return mVisibleRect;
   3160     }
   3161 
   3162     private Point mGlobalVisibleOffset = new Point();
   3163     // Sets r to be the visible rectangle of our webview in view coordinates
   3164     private void calcOurVisibleRect(Rect r) {
   3165         mWebView.getGlobalVisibleRect(r, mGlobalVisibleOffset);
   3166         r.offset(-mGlobalVisibleOffset.x, -mGlobalVisibleOffset.y);
   3167     }
   3168 
   3169     // Sets r to be our visible rectangle in content coordinates
   3170     private void calcOurContentVisibleRect(Rect r) {
   3171         calcOurVisibleRect(r);
   3172         r.left = viewToContentX(r.left);
   3173         // viewToContentY will remove the total height of the title bar.  Add
   3174         // the visible height back in to account for the fact that if the title
   3175         // bar is partially visible, the part of the visible rect which is
   3176         // displaying our content is displaced by that amount.
   3177         r.top = viewToContentY(r.top + getVisibleTitleHeightImpl());
   3178         r.right = viewToContentX(r.right);
   3179         r.bottom = viewToContentY(r.bottom);
   3180     }
   3181 
   3182     private final Rect mTempContentVisibleRect = new Rect();
   3183     // Sets r to be our visible rectangle in content coordinates. We use this
   3184     // method on the native side to compute the position of the fixed layers.
   3185     // Uses floating coordinates (necessary to correctly place elements when
   3186     // the scale factor is not 1)
   3187     private void calcOurContentVisibleRectF(RectF r) {
   3188         calcOurVisibleRect(mTempContentVisibleRect);
   3189         viewToContentVisibleRect(r, mTempContentVisibleRect);
   3190     }
   3191 
   3192     static class ViewSizeData {
   3193         int mWidth;
   3194         int mHeight;
   3195         float mHeightWidthRatio;
   3196         int mActualViewHeight;
   3197         int mTextWrapWidth;
   3198         int mAnchorX;
   3199         int mAnchorY;
   3200         float mScale;
   3201         boolean mIgnoreHeight;
   3202     }
   3203 
   3204     /**
   3205      * Compute unzoomed width and height, and if they differ from the last
   3206      * values we sent, send them to webkit (to be used as new viewport)
   3207      *
   3208      * @param force ensures that the message is sent to webkit even if the width
   3209      * or height has not changed since the last message
   3210      *
   3211      * @return true if new values were sent
   3212      */
   3213     boolean sendViewSizeZoom(boolean force) {
   3214         if (mBlockWebkitViewMessages) return false;
   3215         if (mZoomManager.isPreventingWebkitUpdates()) return false;
   3216 
   3217         int viewWidth = getViewWidth();
   3218         int newWidth = Math.round(viewWidth * mZoomManager.getInvScale());
   3219         // This height could be fixed and be different from actual visible height.
   3220         int viewHeight = getViewHeightWithTitle() - getTitleHeight();
   3221         int newHeight = Math.round(viewHeight * mZoomManager.getInvScale());
   3222         // Make the ratio more accurate than (newHeight / newWidth), since the
   3223         // latter both are calculated and rounded.
   3224         float heightWidthRatio = (float) viewHeight / viewWidth;
   3225         /*
   3226          * Because the native side may have already done a layout before the
   3227          * View system was able to measure us, we have to send a height of 0 to
   3228          * remove excess whitespace when we grow our width. This will trigger a
   3229          * layout and a change in content size. This content size change will
   3230          * mean that contentSizeChanged will either call this method directly or
   3231          * indirectly from onSizeChanged.
   3232          */
   3233         if (newWidth > mLastWidthSent && mWrapContent) {
   3234             newHeight = 0;
   3235             heightWidthRatio = 0;
   3236         }
   3237         // Actual visible content height.
   3238         int actualViewHeight = Math.round(getViewHeight() * mZoomManager.getInvScale());
   3239         // Avoid sending another message if the dimensions have not changed.
   3240         if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force ||
   3241                 actualViewHeight != mLastActualHeightSent) {
   3242             ViewSizeData data = new ViewSizeData();
   3243             data.mWidth = newWidth;
   3244             data.mHeight = newHeight;
   3245             data.mHeightWidthRatio = heightWidthRatio;
   3246             data.mActualViewHeight = actualViewHeight;
   3247             data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.getTextWrapScale());
   3248             data.mScale = mZoomManager.getScale();
   3249             data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress()
   3250                     && !mHeightCanMeasure;
   3251             data.mAnchorX = mZoomManager.getDocumentAnchorX();
   3252             data.mAnchorY = mZoomManager.getDocumentAnchorY();
   3253             mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
   3254             mLastWidthSent = newWidth;
   3255             mLastHeightSent = newHeight;
   3256             mLastActualHeightSent = actualViewHeight;
   3257             mZoomManager.clearDocumentAnchor();
   3258             return true;
   3259         }
   3260         return false;
   3261     }
   3262 
   3263     /**
   3264      * Update the double-tap zoom.
   3265      */
   3266     /* package */ void updateDoubleTapZoom(int doubleTapZoom) {
   3267         mZoomManager.updateDoubleTapZoom(doubleTapZoom);
   3268     }
   3269 
   3270     private int computeRealHorizontalScrollRange() {
   3271         if (mDrawHistory) {
   3272             return mHistoryWidth;
   3273         } else {
   3274             // to avoid rounding error caused unnecessary scrollbar, use floor
   3275             return (int) Math.floor(mContentWidth * mZoomManager.getScale());
   3276         }
   3277     }
   3278 
   3279     @Override
   3280     public int computeHorizontalScrollRange() {
   3281         int range = computeRealHorizontalScrollRange();
   3282 
   3283         // Adjust reported range if overscrolled to compress the scroll bars
   3284         final int scrollX = getScrollX();
   3285         final int overscrollRight = computeMaxScrollX();
   3286         if (scrollX < 0) {
   3287             range -= scrollX;
   3288         } else if (scrollX > overscrollRight) {
   3289             range += scrollX - overscrollRight;
   3290         }
   3291 
   3292         return range;
   3293     }
   3294 
   3295     @Override
   3296     public int computeHorizontalScrollOffset() {
   3297         return Math.max(getScrollX(), 0);
   3298     }
   3299 
   3300     private int computeRealVerticalScrollRange() {
   3301         if (mDrawHistory) {
   3302             return mHistoryHeight;
   3303         } else {
   3304             // to avoid rounding error caused unnecessary scrollbar, use floor
   3305             return (int) Math.floor(mContentHeight * mZoomManager.getScale());
   3306         }
   3307     }
   3308 
   3309     @Override
   3310     public int computeVerticalScrollRange() {
   3311         int range = computeRealVerticalScrollRange();
   3312 
   3313         // Adjust reported range if overscrolled to compress the scroll bars
   3314         final int scrollY = getScrollY();
   3315         final int overscrollBottom = computeMaxScrollY();
   3316         if (scrollY < 0) {
   3317             range -= scrollY;
   3318         } else if (scrollY > overscrollBottom) {
   3319             range += scrollY - overscrollBottom;
   3320         }
   3321 
   3322         return range;
   3323     }
   3324 
   3325     @Override
   3326     public int computeVerticalScrollOffset() {
   3327         return Math.max(getScrollY() - getTitleHeight(), 0);
   3328     }
   3329 
   3330     @Override
   3331     public int computeVerticalScrollExtent() {
   3332         return getViewHeight();
   3333     }
   3334 
   3335     @Override
   3336     public void onDrawVerticalScrollBar(Canvas canvas,
   3337                                            Drawable scrollBar,
   3338                                            int l, int t, int r, int b) {
   3339         if (getScrollY() < 0) {
   3340             t -= getScrollY();
   3341         }
   3342         scrollBar.setBounds(l, t + getVisibleTitleHeightImpl(), r, b);
   3343         scrollBar.draw(canvas);
   3344     }
   3345 
   3346     @Override
   3347     public void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
   3348             boolean clampedY) {
   3349         // Special-case layer scrolling so that we do not trigger normal scroll
   3350         // updating.
   3351         if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
   3352             scrollEditText(scrollX, scrollY);
   3353             return;
   3354         }
   3355         if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
   3356             scrollLayerTo(scrollX, scrollY);
   3357             animateHandles();
   3358             return;
   3359         }
   3360         mInOverScrollMode = false;
   3361         int maxX = computeMaxScrollX();
   3362         int maxY = computeMaxScrollY();
   3363         if (maxX == 0) {
   3364             // do not over scroll x if the page just fits the screen
   3365             scrollX = pinLocX(scrollX);
   3366         } else if (scrollX < 0 || scrollX > maxX) {
   3367             mInOverScrollMode = true;
   3368         }
   3369         if (scrollY < 0 || scrollY > maxY) {
   3370             mInOverScrollMode = true;
   3371         }
   3372 
   3373         int oldX = getScrollX();
   3374         int oldY = getScrollY();
   3375 
   3376         mWebViewPrivate.super_scrollTo(scrollX, scrollY);
   3377 
   3378         animateHandles();
   3379 
   3380         if (mOverScrollGlow != null) {
   3381             mOverScrollGlow.pullGlow(getScrollX(), getScrollY(), oldX, oldY, maxX, maxY);
   3382         }
   3383     }
   3384 
   3385     /**
   3386      * See {@link WebView#getUrl()}
   3387      */
   3388     @Override
   3389     public String getUrl() {
   3390         WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
   3391         return h != null ? h.getUrl() : null;
   3392     }
   3393 
   3394     /**
   3395      * See {@link WebView#getOriginalUrl()}
   3396      */
   3397     @Override
   3398     public String getOriginalUrl() {
   3399         WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
   3400         return h != null ? h.getOriginalUrl() : null;
   3401     }
   3402 
   3403     /**
   3404      * See {@link WebView#getTitle()}
   3405      */
   3406     @Override
   3407     public String getTitle() {
   3408         WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
   3409         return h != null ? h.getTitle() : null;
   3410     }
   3411 
   3412     /**
   3413      * See {@link WebView#getFavicon()}
   3414      */
   3415     @Override
   3416     public Bitmap getFavicon() {
   3417         WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
   3418         return h != null ? h.getFavicon() : null;
   3419     }
   3420 
   3421     /**
   3422      * See {@link WebView#getTouchIconUrl()}
   3423      */
   3424     @Override
   3425     public String getTouchIconUrl() {
   3426         WebHistoryItemClassic h = mCallbackProxy.getBackForwardList().getCurrentItem();
   3427         return h != null ? h.getTouchIconUrl() : null;
   3428     }
   3429 
   3430     /**
   3431      * See {@link WebView#getProgress()}
   3432      */
   3433     @Override
   3434     public int getProgress() {
   3435         return mCallbackProxy.getProgress();
   3436     }
   3437 
   3438     /**
   3439      * See {@link WebView#getContentHeight()}
   3440      */
   3441     @Override
   3442     public int getContentHeight() {
   3443         return mContentHeight;
   3444     }
   3445 
   3446     /**
   3447      * See {@link WebView#getContentWidth()}
   3448      */
   3449     @Override
   3450     public int getContentWidth() {
   3451         return mContentWidth;
   3452     }
   3453 
   3454     public int getPageBackgroundColor() {
   3455         if (mNativeClass == 0) return Color.WHITE;
   3456         return nativeGetBackgroundColor(mNativeClass);
   3457     }
   3458 
   3459     /**
   3460      * See {@link WebView#pauseTimers()}
   3461      */
   3462     @Override
   3463     public void pauseTimers() {
   3464         mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
   3465     }
   3466 
   3467     /**
   3468      * See {@link WebView#resumeTimers()}
   3469      */
   3470     @Override
   3471     public void resumeTimers() {
   3472         mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
   3473     }
   3474 
   3475     /**
   3476      * See {@link WebView#onPause()}
   3477      */
   3478     @Override
   3479     public void onPause() {
   3480         if (!mIsPaused) {
   3481             mIsPaused = true;
   3482             mWebViewCore.sendMessage(EventHub.ON_PAUSE);
   3483             // We want to pause the current playing video when switching out
   3484             // from the current WebView/tab.
   3485             if (mHTML5VideoViewProxy != null) {
   3486                 mHTML5VideoViewProxy.pauseAndDispatch();
   3487             }
   3488             if (mNativeClass != 0) {
   3489                 nativeSetPauseDrawing(mNativeClass, true);
   3490             }
   3491 
   3492             cancelDialogs();
   3493             WebCoreThreadWatchdog.pause();
   3494         }
   3495     }
   3496 
   3497     @Override
   3498     public void onWindowVisibilityChanged(int visibility) {
   3499         updateDrawingState();
   3500     }
   3501 
   3502     void updateDrawingState() {
   3503         if (mNativeClass == 0 || mIsPaused) return;
   3504         if (mWebView.getWindowVisibility() != View.VISIBLE) {
   3505             nativeSetPauseDrawing(mNativeClass, true);
   3506         } else if (mWebView.getVisibility() != View.VISIBLE) {
   3507             nativeSetPauseDrawing(mNativeClass, true);
   3508         } else {
   3509             nativeSetPauseDrawing(mNativeClass, false);
   3510         }
   3511     }
   3512 
   3513     /**
   3514      * See {@link WebView#onResume()}
   3515      */
   3516     @Override
   3517     public void onResume() {
   3518         if (mIsPaused) {
   3519             mIsPaused = false;
   3520             mWebViewCore.sendMessage(EventHub.ON_RESUME);
   3521             if (mNativeClass != 0) {
   3522                 nativeSetPauseDrawing(mNativeClass, false);
   3523             }
   3524         }
   3525         // We get a call to onResume for new WebViews (i.e. mIsPaused will be false). We need
   3526         // to ensure that the Watchdog thread is running for the new WebView, so call
   3527         // it outside the if block above.
   3528         WebCoreThreadWatchdog.resume();
   3529     }
   3530 
   3531     /**
   3532      * See {@link WebView#isPaused()}
   3533      */
   3534     @Override
   3535     public boolean isPaused() {
   3536         return mIsPaused;
   3537     }
   3538 
   3539     /**
   3540      * See {@link WebView#freeMemory()}
   3541      */
   3542     @Override
   3543     public void freeMemory() {
   3544         mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
   3545     }
   3546 
   3547     /**
   3548      * See {@link WebView#clearCache(boolean)}
   3549      */
   3550     @Override
   3551     public void clearCache(boolean includeDiskFiles) {
   3552         // Note: this really needs to be a static method as it clears cache for all
   3553         // WebView. But we need mWebViewCore to send message to WebCore thread, so
   3554         // we can't make this static.
   3555         mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
   3556                 includeDiskFiles ? 1 : 0, 0);
   3557     }
   3558 
   3559     /**
   3560      * See {@link WebView#clearFormData()}
   3561      */
   3562     @Override
   3563     public void clearFormData() {
   3564         if (mAutoCompletePopup != null) {
   3565             mAutoCompletePopup.clearAdapter();
   3566         }
   3567     }
   3568 
   3569     /**
   3570      * See {@link WebView#clearHistory()}
   3571      */
   3572     @Override
   3573     public void clearHistory() {
   3574         mCallbackProxy.getBackForwardList().setClearPending();
   3575         mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
   3576     }
   3577 
   3578     /**
   3579      * See {@link WebView#clearSslPreferences()}
   3580      */
   3581     @Override
   3582     public void clearSslPreferences() {
   3583         mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
   3584     }
   3585 
   3586     /**
   3587      * See {@link WebView#copyBackForwardList()}
   3588      */
   3589     @Override
   3590     public WebBackForwardListClassic copyBackForwardList() {
   3591         return mCallbackProxy.getBackForwardList().clone();
   3592     }
   3593 
   3594     /**
   3595      * See {@link WebView#setFindListener(WebView.FindListener)}.
   3596      * @hide
   3597      */
   3598      @Override
   3599     public void setFindListener(WebView.FindListener listener) {
   3600          mFindListener = listener;
   3601      }
   3602 
   3603     /**
   3604      * See {@link WebView#findNext(boolean)}
   3605      */
   3606     @Override
   3607     public void findNext(boolean forward) {
   3608         if (0 == mNativeClass) return; // client isn't initialized
   3609         if (mFindRequest != null) {
   3610             mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0, mFindRequest);
   3611         }
   3612     }
   3613 
   3614     /**
   3615      * See {@link WebView#findAll(String)}
   3616      */
   3617     @Override
   3618     public int findAll(String find) {
   3619         return findAllBody(find, false);
   3620     }
   3621 
   3622     @Override
   3623     public void findAllAsync(String find) {
   3624         findAllBody(find, true);
   3625     }
   3626 
   3627     private int findAllBody(String find, boolean isAsync) {
   3628         if (0 == mNativeClass) return 0; // client isn't initialized
   3629         mFindRequest = null;
   3630         if (find == null) return 0;
   3631         mWebViewCore.removeMessages(EventHub.FIND_ALL);
   3632         mFindRequest = new WebViewCore.FindAllRequest(find);
   3633         if (isAsync) {
   3634             mWebViewCore.sendMessage(EventHub.FIND_ALL, mFindRequest);
   3635             return 0; // no need to wait for response
   3636         }
   3637         synchronized(mFindRequest) {
   3638             try {
   3639                 mWebViewCore.sendMessageAtFrontOfQueue(EventHub.FIND_ALL, mFindRequest);
   3640                 while (mFindRequest.mMatchCount == -1) {
   3641                     mFindRequest.wait();
   3642                 }
   3643             }
   3644             catch (InterruptedException e) {
   3645                 return 0;
   3646             }
   3647             return mFindRequest.mMatchCount;
   3648         }
   3649     }
   3650 
   3651     /**
   3652      * Start an ActionMode for finding text in this WebView.  Only works if this
   3653      *              WebView is attached to the view system.
   3654      * @param text If non-null, will be the initial text to search for.
   3655      *             Otherwise, the last String searched for in this WebView will
   3656      *             be used to start.
   3657      * @param showIme If true, show the IME, assuming the user will begin typing.
   3658      *             If false and text is non-null, perform a find all.
   3659      * @return boolean True if the find dialog is shown, false otherwise.
   3660      */
   3661     @Override
   3662     public boolean showFindDialog(String text, boolean showIme) {
   3663         FindActionModeCallback callback = new FindActionModeCallback(mContext);
   3664         if (mWebView.getParent() == null || mWebView.startActionMode(callback) == null) {
   3665             // Could not start the action mode, so end Find on page
   3666             return false;
   3667         }
   3668         mCachedOverlappingActionModeHeight = -1;
   3669         mFindCallback = callback;
   3670         setFindIsUp(true);
   3671         mFindCallback.setWebView(this);
   3672         if (showIme) {
   3673             mFindCallback.showSoftInput();
   3674         } else if (text != null) {
   3675             mFindCallback.setText(text);
   3676             mFindCallback.findAll();
   3677             return true;
   3678         }
   3679         if (text == null) {
   3680             text = mFindRequest == null ? null : mFindRequest.mSearchText;
   3681         }
   3682         if (text != null) {
   3683             mFindCallback.setText(text);
   3684             mFindCallback.findAll();
   3685         }
   3686         return true;
   3687     }
   3688 
   3689     /**
   3690      * Keep track of the find callback so that we can remove its titlebar if
   3691      * necessary.
   3692      */
   3693     private FindActionModeCallback mFindCallback;
   3694 
   3695     /**
   3696      * Toggle whether the find dialog is showing, for both native and Java.
   3697      */
   3698     private void setFindIsUp(boolean isUp) {
   3699         mFindIsUp = isUp;
   3700     }
   3701 
   3702     // Used to know whether the find dialog is open.  Affects whether
   3703     // or not we draw the highlights for matches.
   3704     private boolean mFindIsUp;
   3705 
   3706     // Keep track of the last find request sent.
   3707     private WebViewCore.FindAllRequest mFindRequest = null;
   3708 
   3709     /**
   3710      * Return the first substring consisting of the address of a physical
   3711      * location. Currently, only addresses in the United States are detected,
   3712      * and consist of:
   3713      * - a house number
   3714      * - a street name
   3715      * - a street type (Road, Circle, etc), either spelled out or abbreviated
   3716      * - a city name
   3717      * - a state or territory, either spelled out or two-letter abbr.
   3718      * - an optional 5 digit or 9 digit zip code.
   3719      *
   3720      * All names must be correctly capitalized, and the zip code, if present,
   3721      * must be valid for the state. The street type must be a standard USPS
   3722      * spelling or abbreviation. The state or territory must also be spelled
   3723      * or abbreviated using USPS standards. The house number may not exceed
   3724      * five digits.
   3725      * @param addr The string to search for addresses.
   3726      *
   3727      * @return the address, or if no address is found, return null.
   3728      */
   3729     public static String findAddress(String addr) {
   3730         return findAddress(addr, false);
   3731     }
   3732 
   3733     /**
   3734      * Return the first substring consisting of the address of a physical
   3735      * location. Currently, only addresses in the United States are detected,
   3736      * and consist of:
   3737      * - a house number
   3738      * - a street name
   3739      * - a street type (Road, Circle, etc), either spelled out or abbreviated
   3740      * - a city name
   3741      * - a state or territory, either spelled out or two-letter abbr.
   3742      * - an optional 5 digit or 9 digit zip code.
   3743      *
   3744      * Names are optionally capitalized, and the zip code, if present,
   3745      * must be valid for the state. The street type must be a standard USPS
   3746      * spelling or abbreviation. The state or territory must also be spelled
   3747      * or abbreviated using USPS standards. The house number may not exceed
   3748      * five digits.
   3749      * @param addr The string to search for addresses.
   3750      * @param caseInsensitive addr Set to true to make search ignore case.
   3751      *
   3752      * @return the address, or if no address is found, return null.
   3753      */
   3754     public static String findAddress(String addr, boolean caseInsensitive) {
   3755         return WebViewCore.nativeFindAddress(addr, caseInsensitive);
   3756     }
   3757 
   3758     /**
   3759      * See {@link WebView#clearMatches()}
   3760      */
   3761     @Override
   3762     public void clearMatches() {
   3763         if (mNativeClass == 0)
   3764             return;
   3765         mWebViewCore.removeMessages(EventHub.FIND_ALL);
   3766         mWebViewCore.sendMessage(EventHub.FIND_ALL, null);
   3767     }
   3768 
   3769 
   3770     /**
   3771      * Called when the find ActionMode ends.
   3772      */
   3773     void notifyFindDialogDismissed() {
   3774         mFindCallback = null;
   3775         mCachedOverlappingActionModeHeight = -1;
   3776         if (mWebViewCore == null) {
   3777             return;
   3778         }
   3779         clearMatches();
   3780         setFindIsUp(false);
   3781         // Now that the dialog has been removed, ensure that we scroll to a
   3782         // location that is not beyond the end of the page.
   3783         pinScrollTo(getScrollX(), getScrollY(), false, 0);
   3784         invalidate();
   3785     }
   3786 
   3787     /**
   3788      * See {@link WebView#documentHasImages(Message)}
   3789      */
   3790     @Override
   3791     public void documentHasImages(Message response) {
   3792         if (response == null) {
   3793             return;
   3794         }
   3795         mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
   3796     }
   3797 
   3798     /**
   3799      * Request the scroller to abort any ongoing animation
   3800      */
   3801     public void stopScroll() {
   3802         mScroller.forceFinished(true);
   3803         mLastVelocity = 0;
   3804     }
   3805 
   3806     @Override
   3807     public void computeScroll() {
   3808         if (mScroller.computeScrollOffset()) {
   3809             int oldX = getScrollX();
   3810             int oldY = getScrollY();
   3811             int x = mScroller.getCurrX();
   3812             int y = mScroller.getCurrY();
   3813             invalidate();  // So we draw again
   3814 
   3815             if (!mScroller.isFinished()) {
   3816                 int rangeX = computeMaxScrollX();
   3817                 int rangeY = computeMaxScrollY();
   3818                 int overflingDistance = mOverflingDistance;
   3819 
   3820                 // Use the layer's scroll data if needed.
   3821                 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
   3822                     oldX = mScrollingLayerRect.left;
   3823                     oldY = mScrollingLayerRect.top;
   3824                     rangeX = mScrollingLayerRect.right;
   3825                     rangeY = mScrollingLayerRect.bottom;
   3826                     // No overscrolling for layers.
   3827                     overflingDistance = 0;
   3828                 } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
   3829                     oldX = getTextScrollX();
   3830                     oldY = getTextScrollY();
   3831                     rangeX = getMaxTextScrollX();
   3832                     rangeY = getMaxTextScrollY();
   3833                     overflingDistance = 0;
   3834                 }
   3835 
   3836                 mWebViewPrivate.overScrollBy(x - oldX, y - oldY, oldX, oldY,
   3837                         rangeX, rangeY,
   3838                         overflingDistance, overflingDistance, false);
   3839 
   3840                 if (mOverScrollGlow != null) {
   3841                     mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY);
   3842                 }
   3843             } else {
   3844                 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
   3845                     // Update the layer position instead of WebView.
   3846                     scrollLayerTo(x, y);
   3847                 } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
   3848                     scrollEditText(x, y);
   3849                 } else {
   3850                     setScrollXRaw(x);
   3851                     setScrollYRaw(y);
   3852                 }
   3853                 abortAnimation();
   3854                 nativeSetIsScrolling(false);
   3855                 if (!mBlockWebkitViewMessages) {
   3856                     WebViewCore.resumePriority();
   3857                     if (!mSelectingText) {
   3858                         WebViewCore.resumeUpdatePicture(mWebViewCore);
   3859                     }
   3860                 }
   3861                 if (oldX != getScrollX() || oldY != getScrollY()) {
   3862                     sendOurVisibleRect();
   3863                 }
   3864             }
   3865         } else {
   3866             mWebViewPrivate.super_computeScroll();
   3867         }
   3868     }
   3869 
   3870     private void scrollLayerTo(int x, int y) {
   3871         int dx = mScrollingLayerRect.left - x;
   3872         int dy = mScrollingLayerRect.top - y;
   3873         if ((dx == 0 && dy == 0) || mNativeClass == 0) {
   3874             return;
   3875         }
   3876         if (mSelectingText) {
   3877             if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) {
   3878                 mSelectCursorBase.offset(dx, dy);
   3879                 mSelectCursorBaseTextQuad.offset(dx, dy);
   3880             }
   3881             if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) {
   3882                 mSelectCursorExtent.offset(dx, dy);
   3883                 mSelectCursorExtentTextQuad.offset(dx, dy);
   3884             }
   3885         }
   3886         if (mAutoCompletePopup != null &&
   3887                 mCurrentScrollingLayerId == mEditTextLayerId) {
   3888             mEditTextContentBounds.offset(dx, dy);
   3889             mAutoCompletePopup.resetRect();
   3890         }
   3891         nativeScrollLayer(mNativeClass, mCurrentScrollingLayerId, x, y);
   3892         mScrollingLayerRect.left = x;
   3893         mScrollingLayerRect.top = y;
   3894         mWebViewCore.sendMessage(WebViewCore.EventHub.SCROLL_LAYER, mCurrentScrollingLayerId,
   3895                 mScrollingLayerRect);
   3896         mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());
   3897         invalidate();
   3898     }
   3899 
   3900     private static int computeDuration(int dx, int dy) {
   3901         int distance = Math.max(Math.abs(dx), Math.abs(dy));
   3902         int duration = distance * 1000 / STD_SPEED;
   3903         return Math.min(duration, MAX_DURATION);
   3904     }
   3905 
   3906     // helper to pin the scrollBy parameters (already in view coordinates)
   3907     // returns true if the scroll was changed
   3908     private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
   3909         return pinScrollTo(getScrollX() + dx, getScrollY() + dy, animate, animationDuration);
   3910     }
   3911     // helper to pin the scrollTo parameters (already in view coordinates)
   3912     // returns true if the scroll was changed
   3913     private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
   3914         abortAnimation();
   3915         x = pinLocX(x);
   3916         y = pinLocY(y);
   3917         int dx = x - getScrollX();
   3918         int dy = y - getScrollY();
   3919 
   3920         if ((dx | dy) == 0) {
   3921             return false;
   3922         }
   3923         if (animate) {
   3924             //        Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
   3925             mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,
   3926                     animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
   3927             invalidate();
   3928         } else {
   3929             mWebView.scrollTo(x, y);
   3930         }
   3931         return true;
   3932     }
   3933 
   3934     // Scale from content to view coordinates, and pin.
   3935     // Also called by jni webview.cpp
   3936     private boolean setContentScrollBy(int cx, int cy, boolean animate) {
   3937         if (mDrawHistory) {
   3938             // disallow WebView to change the scroll position as History Picture
   3939             // is used in the view system.
   3940             // TODO: as we switchOutDrawHistory when trackball or navigation
   3941             // keys are hit, this should be safe. Right?
   3942             return false;
   3943         }
   3944         cx = contentToViewDimension(cx);
   3945         cy = contentToViewDimension(cy);
   3946         if (mHeightCanMeasure) {
   3947             // move our visible rect according to scroll request
   3948             if (cy != 0) {
   3949                 Rect tempRect = new Rect();
   3950                 calcOurVisibleRect(tempRect);
   3951                 tempRect.offset(cx, cy);
   3952                 mWebView.requestRectangleOnScreen(tempRect);
   3953             }
   3954             // FIXME: We scroll horizontally no matter what because currently
   3955             // ScrollView and ListView will not scroll horizontally.
   3956             // FIXME: Why do we only scroll horizontally if there is no
   3957             // vertical scroll?
   3958 //                Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
   3959             return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
   3960         } else {
   3961             return pinScrollBy(cx, cy, animate, 0);
   3962         }
   3963     }
   3964 
   3965     /**
   3966      * Called by CallbackProxy when the page starts loading.
   3967      * @param url The URL of the page which has started loading.
   3968      */
   3969     /* package */ void onPageStarted(String url) {
   3970         // every time we start a new page, we want to reset the
   3971         // WebView certificate:  if the new site is secure, we
   3972         // will reload it and get a new certificate set;
   3973         // if the new site is not secure, the certificate must be
   3974         // null, and that will be the case
   3975         mWebView.setCertificate(null);
   3976 
   3977         if (isAccessibilityInjectionEnabled()) {
   3978             getAccessibilityInjector().onPageStarted(url);
   3979         }
   3980 
   3981         // Don't start out editing.
   3982         mIsEditingText = false;
   3983     }
   3984 
   3985     /**
   3986      * Called by CallbackProxy when the page finishes loading.
   3987      * @param url The URL of the page which has finished loading.
   3988      */
   3989     /* package */ void onPageFinished(String url) {
   3990         mZoomManager.onPageFinished(url);
   3991 
   3992         if (isAccessibilityInjectionEnabled()) {
   3993             getAccessibilityInjector().onPageFinished(url);
   3994         }
   3995     }
   3996 
   3997     // scale from content to view coordinates, and pin
   3998     private void contentScrollTo(int cx, int cy, boolean animate) {
   3999         if (mDrawHistory) {
   4000             // disallow WebView to change the scroll position as History Picture
   4001             // is used in the view system.
   4002             return;
   4003         }
   4004         int vx = contentToViewX(cx);
   4005         int vy = contentToViewY(cy);
   4006         pinScrollTo(vx, vy, animate, 0);
   4007     }
   4008 
   4009     /**
   4010      * These are from webkit, and are in content coordinate system (unzoomed)
   4011      */
   4012     private void contentSizeChanged(boolean updateLayout) {
   4013         // suppress 0,0 since we usually see real dimensions soon after
   4014         // this avoids drawing the prev content in a funny place. If we find a
   4015         // way to consolidate these notifications, this check may become
   4016         // obsolete
   4017         if ((mContentWidth | mContentHeight) == 0) {
   4018             return;
   4019         }
   4020 
   4021         if (mHeightCanMeasure) {
   4022             if (mWebView.getMeasuredHeight() != contentToViewDimension(mContentHeight)
   4023                     || updateLayout) {
   4024                 mWebView.requestLayout();
   4025             }
   4026         } else if (mWidthCanMeasure) {
   4027             if (mWebView.getMeasuredWidth() != contentToViewDimension(mContentWidth)
   4028                     || updateLayout) {
   4029                 mWebView.requestLayout();
   4030             }
   4031         } else {
   4032             // If we don't request a layout, try to send our view size to the
   4033             // native side to ensure that WebCore has the correct dimensions.
   4034             sendViewSizeZoom(false);
   4035         }
   4036     }
   4037 
   4038     /**
   4039      * See {@link WebView#setWebViewClient(WebViewClient)}
   4040      */
   4041     @Override
   4042     public void setWebViewClient(WebViewClient client) {
   4043         mCallbackProxy.setWebViewClient(client);
   4044     }
   4045 
   4046     /**
   4047      * Gets the WebViewClient
   4048      * @return the current WebViewClient instance.
   4049      *
   4050      * This is an implementation detail.
   4051      */
   4052     public WebViewClient getWebViewClient() {
   4053         return mCallbackProxy.getWebViewClient();
   4054     }
   4055 
   4056     /**
   4057      * See {@link WebView#setDownloadListener(DownloadListener)}
   4058      */
   4059     @Override
   4060     public void setDownloadListener(DownloadListener listener) {
   4061         mCallbackProxy.setDownloadListener(listener);
   4062     }
   4063 
   4064     /**
   4065      * See {@link WebView#setWebChromeClient(WebChromeClient)}
   4066      */
   4067     @Override
   4068     public void setWebChromeClient(WebChromeClient client) {
   4069         mCallbackProxy.setWebChromeClient(client);
   4070     }
   4071 
   4072     /**
   4073      * Gets the chrome handler.
   4074      * @return the current WebChromeClient instance.
   4075      *
   4076      * This is an implementation detail.
   4077      */
   4078     public WebChromeClient getWebChromeClient() {
   4079         return mCallbackProxy.getWebChromeClient();
   4080     }
   4081 
   4082     /**
   4083      * Set the back/forward list client. This is an implementation of
   4084      * WebBackForwardListClient for handling new items and changes in the
   4085      * history index.
   4086      * @param client An implementation of WebBackForwardListClient.
   4087      */
   4088     public void setWebBackForwardListClient(WebBackForwardListClient client) {
   4089         mCallbackProxy.setWebBackForwardListClient(client);
   4090     }
   4091 
   4092     /**
   4093      * Gets the WebBackForwardListClient.
   4094      */
   4095     public WebBackForwardListClient getWebBackForwardListClient() {
   4096         return mCallbackProxy.getWebBackForwardListClient();
   4097     }
   4098 
   4099     /**
   4100      * See {@link WebView#setPictureListener(PictureListener)}
   4101      */
   4102     @Override
   4103     @Deprecated
   4104     public void setPictureListener(PictureListener listener) {
   4105         mPictureListener = listener;
   4106     }
   4107 
   4108     /* FIXME: Debug only! Remove for SDK! */
   4109     public void externalRepresentation(Message callback) {
   4110         mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
   4111     }
   4112 
   4113     /* FIXME: Debug only! Remove for SDK! */
   4114     public void documentAsText(Message callback) {
   4115         mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
   4116     }
   4117 
   4118     /**
   4119      * See {@link WebView#addJavascriptInterface(Object, String)}
   4120      */
   4121     @Override
   4122     public void addJavascriptInterface(Object object, String name) {
   4123 
   4124         if (object == null) {
   4125             return;
   4126         }
   4127         WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
   4128 
   4129         arg.mObject = object;
   4130         arg.mInterfaceName = name;
   4131 
   4132         // starting with JELLY_BEAN_MR1, annotations are mandatory for enabling access to
   4133         // methods that are accessible from JS.
   4134         if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
   4135             arg.mRequireAnnotation = true;
   4136         } else {
   4137             arg.mRequireAnnotation = false;
   4138         }
   4139         mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
   4140     }
   4141 
   4142     /**
   4143      * See {@link WebView#removeJavascriptInterface(String)}
   4144      */
   4145     @Override
   4146     public void removeJavascriptInterface(String interfaceName) {
   4147         if (mWebViewCore != null) {
   4148             WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
   4149             arg.mInterfaceName = interfaceName;
   4150             mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
   4151         }
   4152     }
   4153 
   4154     /**
   4155      * See {@link WebView#getSettings()}
   4156      * Note this returns WebSettingsClassic, a sub-class of WebSettings, which can be used
   4157      * to access extension APIs.
   4158      */
   4159     @Override
   4160     public WebSettingsClassic getSettings() {
   4161         return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
   4162     }
   4163 
   4164     /**
   4165      * See {@link WebView#getPluginList()}
   4166      */
   4167     @Deprecated
   4168     public static synchronized PluginList getPluginList() {
   4169         return new PluginList();
   4170     }
   4171 
   4172     /**
   4173      * See {@link WebView#refreshPlugins(boolean)}
   4174      */
   4175     @Deprecated
   4176     public void refreshPlugins(boolean reloadOpenPages) {
   4177     }
   4178 
   4179     //-------------------------------------------------------------------------
   4180     // Override View methods
   4181     //-------------------------------------------------------------------------
   4182 
   4183     @Override
   4184     protected void finalize() throws Throwable {
   4185         try {
   4186             destroy();
   4187         } finally {
   4188             super.finalize();
   4189         }
   4190     }
   4191 
   4192     private void drawContent(Canvas canvas) {
   4193         if (mDrawHistory) {
   4194             canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
   4195             canvas.drawPicture(mHistoryPicture);
   4196             return;
   4197         }
   4198         if (mNativeClass == 0) return;
   4199 
   4200         boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress();
   4201         boolean animateScroll = ((!mScroller.isFinished()
   4202                 || mVelocityTracker != null)
   4203                 && (mTouchMode != TOUCH_DRAG_MODE ||
   4204                 mHeldMotionless != MOTIONLESS_TRUE));
   4205         if (mTouchMode == TOUCH_DRAG_MODE) {
   4206             if (mHeldMotionless == MOTIONLESS_PENDING) {
   4207                 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
   4208                 mHeldMotionless = MOTIONLESS_FALSE;
   4209             }
   4210             if (mHeldMotionless == MOTIONLESS_FALSE) {
   4211                 mPrivateHandler.sendMessageDelayed(mPrivateHandler
   4212                         .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
   4213                 mHeldMotionless = MOTIONLESS_PENDING;
   4214             }
   4215         }
   4216         int saveCount = canvas.save();
   4217         if (animateZoom) {
   4218             mZoomManager.animateZoom(canvas);
   4219         } else if (!canvas.isHardwareAccelerated()) {
   4220             canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
   4221         }
   4222 
   4223         boolean UIAnimationsRunning = false;
   4224         // Currently for each draw we compute the animation values;
   4225         // We may in the future decide to do that independently.
   4226         if (mNativeClass != 0 && !canvas.isHardwareAccelerated()
   4227                 && nativeEvaluateLayersAnimations(mNativeClass)) {
   4228             UIAnimationsRunning = true;
   4229             // If we have unfinished (or unstarted) animations,
   4230             // we ask for a repaint. We only need to do this in software
   4231             // rendering (with hardware rendering we already have a different
   4232             // method of requesting a repaint)
   4233             mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
   4234             invalidate();
   4235         }
   4236 
   4237         // decide which adornments to draw
   4238         int extras = DRAW_EXTRAS_NONE;
   4239         if (!mFindIsUp && mShowTextSelectionExtra) {
   4240             extras = DRAW_EXTRAS_SELECTION;
   4241         }
   4242 
   4243         calcOurContentVisibleRectF(mVisibleContentRect);
   4244         if (canvas.isHardwareAccelerated()) {
   4245             Rect invScreenRect = mIsWebViewVisible ? mInvScreenRect : null;
   4246             Rect screenRect = mIsWebViewVisible ? mScreenRect : null;
   4247 
   4248             int functor = nativeCreateDrawGLFunction(mNativeClass, invScreenRect,
   4249                     screenRect, mVisibleContentRect, getScale(), extras);
   4250             ((HardwareCanvas) canvas).callDrawGLFunction(functor);
   4251             if (mHardwareAccelSkia != getSettings().getHardwareAccelSkiaEnabled()) {
   4252                 mHardwareAccelSkia = getSettings().getHardwareAccelSkiaEnabled();
   4253                 nativeUseHardwareAccelSkia(mHardwareAccelSkia);
   4254             }
   4255 
   4256         } else {
   4257             DrawFilter df = null;
   4258             if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
   4259                 df = mZoomFilter;
   4260             } else if (animateScroll) {
   4261                 df = mScrollFilter;
   4262             }
   4263             canvas.setDrawFilter(df);
   4264             nativeDraw(canvas, mVisibleContentRect, mBackgroundColor, extras);
   4265             canvas.setDrawFilter(null);
   4266         }
   4267 
   4268         canvas.restoreToCount(saveCount);
   4269         drawTextSelectionHandles(canvas);
   4270 
   4271         if (extras == DRAW_EXTRAS_CURSOR_RING) {
   4272             if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
   4273                 mTouchMode = TOUCH_SHORTPRESS_MODE;
   4274             }
   4275         }
   4276     }
   4277 
   4278     /**
   4279      * Draw the background when beyond bounds
   4280      * @param canvas Canvas to draw into
   4281      */
   4282     private void drawOverScrollBackground(Canvas canvas) {
   4283         if (mOverScrollBackground == null) {
   4284             mOverScrollBackground = new Paint();
   4285             Bitmap bm = BitmapFactory.decodeResource(
   4286                     mContext.getResources(),
   4287                     com.android.internal.R.drawable.status_bar_background);
   4288             mOverScrollBackground.setShader(new BitmapShader(bm,
   4289                     Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
   4290             mOverScrollBorder = new Paint();
   4291             mOverScrollBorder.setStyle(Paint.Style.STROKE);
   4292             mOverScrollBorder.setStrokeWidth(0);
   4293             mOverScrollBorder.setColor(0xffbbbbbb);
   4294         }
   4295 
   4296         int top = 0;
   4297         int right = computeRealHorizontalScrollRange();
   4298         int bottom = top + computeRealVerticalScrollRange();
   4299         // first draw the background and anchor to the top of the view
   4300         canvas.save();
   4301         canvas.translate(getScrollX(), getScrollY());
   4302         canvas.clipRect(-getScrollX(), top - getScrollY(), right - getScrollX(), bottom
   4303                 - getScrollY(), Region.Op.DIFFERENCE);
   4304         canvas.drawPaint(mOverScrollBackground);
   4305         canvas.restore();
   4306         // then draw the border
   4307         canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
   4308         // next clip the region for the content
   4309         canvas.clipRect(0, top, right, bottom);
   4310     }
   4311 
   4312     @Override
   4313     public void onDraw(Canvas canvas) {
   4314         if (inFullScreenMode()) {
   4315             return; // no need to draw anything if we aren't visible.
   4316         }
   4317         // if mNativeClass is 0, the WebView is either destroyed or not
   4318         // initialized. In either case, just draw the background color and return
   4319         if (mNativeClass == 0) {
   4320             canvas.drawColor(mBackgroundColor);
   4321             return;
   4322         }
   4323 
   4324         // if both mContentWidth and mContentHeight are 0, it means there is no
   4325         // valid Picture passed to WebView yet. This can happen when WebView
   4326         // just starts. Draw the background and return.
   4327         if ((mContentWidth | mContentHeight) == 0 && mHistoryPicture == null) {
   4328             canvas.drawColor(mBackgroundColor);
   4329             return;
   4330         }
   4331 
   4332         if (canvas.isHardwareAccelerated()) {
   4333             mZoomManager.setHardwareAccelerated();
   4334         } else {
   4335             mWebViewCore.resumeWebKitDraw();
   4336         }
   4337 
   4338         int saveCount = canvas.save();
   4339         if (mInOverScrollMode && !getSettings()
   4340                 .getUseWebViewBackgroundForOverscrollBackground()) {
   4341             drawOverScrollBackground(canvas);
   4342         }
   4343 
   4344         canvas.translate(0, getTitleHeight());
   4345         drawContent(canvas);
   4346         canvas.restoreToCount(saveCount);
   4347 
   4348         if (AUTO_REDRAW_HACK && mAutoRedraw) {
   4349             invalidate();
   4350         }
   4351         mWebViewCore.signalRepaintDone();
   4352 
   4353         if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) {
   4354             invalidate();
   4355         }
   4356 
   4357         if (mFocusTransition != null) {
   4358             mFocusTransition.draw(canvas);
   4359         } else if (shouldDrawHighlightRect()) {
   4360             RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
   4361             Rect r = new Rect();
   4362             while (iter.next(r)) {
   4363                 canvas.drawRect(r, mTouchHightlightPaint);
   4364             }
   4365         }
   4366         if (DEBUG_TOUCH_HIGHLIGHT) {
   4367             if (getSettings().getNavDump()) {
   4368                 if ((mTouchHighlightX | mTouchHighlightY) != 0) {
   4369                     if (mTouchCrossHairColor == null) {
   4370                         mTouchCrossHairColor = new Paint();
   4371                         mTouchCrossHairColor.setColor(Color.RED);
   4372                     }
   4373                     canvas.drawLine(mTouchHighlightX - mNavSlop,
   4374                             mTouchHighlightY - mNavSlop, mTouchHighlightX
   4375                                     + mNavSlop + 1, mTouchHighlightY + mNavSlop
   4376                                     + 1, mTouchCrossHairColor);
   4377                     canvas.drawLine(mTouchHighlightX + mNavSlop + 1,
   4378                             mTouchHighlightY - mNavSlop, mTouchHighlightX
   4379                                     - mNavSlop,
   4380                             mTouchHighlightY + mNavSlop + 1,
   4381                             mTouchCrossHairColor);
   4382                 }
   4383             }
   4384         }
   4385     }
   4386 
   4387     private void removeTouchHighlight() {
   4388         setTouchHighlightRects(null);
   4389     }
   4390 
   4391     @Override
   4392     public void setLayoutParams(ViewGroup.LayoutParams params) {
   4393         if (params.height == AbsoluteLayout.LayoutParams.WRAP_CONTENT) {
   4394             mWrapContent = true;
   4395         }
   4396         mWebViewPrivate.super_setLayoutParams(params);
   4397     }
   4398 
   4399     @Override
   4400     public boolean performLongClick() {
   4401         // performLongClick() is the result of a delayed message. If we switch
   4402         // to windows overview, the WebView will be temporarily removed from the
   4403         // view system. In that case, do nothing.
   4404         if (mWebView.getParent() == null) return false;
   4405 
   4406         // A multi-finger gesture can look like a long press; make sure we don't take
   4407         // long press actions if we're scaling.
   4408         final ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
   4409         if (detector != null && detector.isInProgress()) {
   4410             return false;
   4411         }
   4412 
   4413         if (mSelectingText) return false; // long click does nothing on selection
   4414         /* if long click brings up a context menu, the super function
   4415          * returns true and we're done. Otherwise, nothing happened when
   4416          * the user clicked. */
   4417         if (mWebViewPrivate.super_performLongClick()) {
   4418             return true;
   4419         }
   4420         /* In the case where the application hasn't already handled the long
   4421          * click action, look for a word under the  click. If one is found,
   4422          * animate the text selection into view.
   4423          * FIXME: no animation code yet */
   4424         final boolean isSelecting = selectText();
   4425         if (isSelecting) {
   4426             mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
   4427         } else if (focusCandidateIsEditableText()) {
   4428             mSelectCallback = new SelectActionModeCallback();
   4429             mSelectCallback.setWebView(this);
   4430             mSelectCallback.setTextSelected(false);
   4431             mWebView.startActionMode(mSelectCallback);
   4432         }
   4433         return isSelecting;
   4434     }
   4435 
   4436     /**
   4437      * Select the word at the last click point.
   4438      *
   4439      * This is an implementation detail.
   4440      */
   4441     public boolean selectText() {
   4442         int x = viewToContentX(mLastTouchX + getScrollX());
   4443         int y = viewToContentY(mLastTouchY + getScrollY());
   4444         return selectText(x, y);
   4445     }
   4446 
   4447     /**
   4448      * Select the word at the indicated content coordinates.
   4449      */
   4450     boolean selectText(int x, int y) {
   4451         if (mWebViewCore == null) {
   4452             return false;
   4453         }
   4454         mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y);
   4455         return true;
   4456     }
   4457 
   4458     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
   4459 
   4460     @Override
   4461     public void onConfigurationChanged(Configuration newConfig) {
   4462         mCachedOverlappingActionModeHeight = -1;
   4463         if (mSelectingText && mOrientation != newConfig.orientation) {
   4464             selectionDone();
   4465         }
   4466         mOrientation = newConfig.orientation;
   4467         if (mWebViewCore != null && !mBlockWebkitViewMessages) {
   4468             mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
   4469         }
   4470     }
   4471 
   4472     /**
   4473      * Keep track of the Callback so we can end its ActionMode or remove its
   4474      * titlebar.
   4475      */
   4476     private SelectActionModeCallback mSelectCallback;
   4477 
   4478     void setBaseLayer(int layer, boolean showVisualIndicator,
   4479             boolean isPictureAfterFirstLayout) {
   4480         if (mNativeClass == 0)
   4481             return;
   4482         boolean queueFull;
   4483         final int scrollingLayer = (mTouchMode == TOUCH_DRAG_LAYER_MODE)
   4484                 ? mCurrentScrollingLayerId : 0;
   4485         queueFull = nativeSetBaseLayer(mNativeClass, layer,
   4486                                        showVisualIndicator, isPictureAfterFirstLayout,
   4487                                        scrollingLayer);
   4488 
   4489         if (queueFull) {
   4490             mWebViewCore.pauseWebKitDraw();
   4491         } else {
   4492             mWebViewCore.resumeWebKitDraw();
   4493         }
   4494 
   4495         if (mHTML5VideoViewProxy != null) {
   4496             mHTML5VideoViewProxy.setBaseLayer(layer);
   4497         }
   4498     }
   4499 
   4500     int getBaseLayer() {
   4501         if (mNativeClass == 0) {
   4502             return 0;
   4503         }
   4504         return nativeGetBaseLayer(mNativeClass);
   4505     }
   4506 
   4507     private void onZoomAnimationStart() {
   4508     }
   4509 
   4510     private void onZoomAnimationEnd() {
   4511         mPrivateHandler.sendEmptyMessage(RELOCATE_AUTO_COMPLETE_POPUP);
   4512     }
   4513 
   4514     void onFixedLengthZoomAnimationStart() {
   4515         WebViewCore.pauseUpdatePicture(getWebViewCore());
   4516         onZoomAnimationStart();
   4517     }
   4518 
   4519     void onFixedLengthZoomAnimationEnd() {
   4520         if (!mBlockWebkitViewMessages && !mSelectingText) {
   4521             WebViewCore.resumeUpdatePicture(mWebViewCore);
   4522         }
   4523         onZoomAnimationEnd();
   4524     }
   4525 
   4526     private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
   4527                                          Paint.DITHER_FLAG |
   4528                                          Paint.SUBPIXEL_TEXT_FLAG;
   4529     private static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
   4530                                            Paint.DITHER_FLAG;
   4531 
   4532     private final DrawFilter mZoomFilter =
   4533             new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
   4534     // If we need to trade better quality for speed, set mScrollFilter to null
   4535     private final DrawFilter mScrollFilter =
   4536             new PaintFlagsDrawFilter(SCROLL_BITS, 0);
   4537 
   4538     private class SelectionHandleAlpha {
   4539         private int mAlpha = 0;
   4540         private int mTargetAlpha = 0;
   4541 
   4542         public void setAlpha(int alpha) {
   4543             mAlpha = alpha;
   4544             // TODO: Use partial invalidate
   4545             invalidate();
   4546         }
   4547 
   4548         public int getAlpha() {
   4549             return mAlpha;
   4550         }
   4551 
   4552         public void setTargetAlpha(int alpha) {
   4553             mTargetAlpha = alpha;
   4554         }
   4555 
   4556         public int getTargetAlpha() {
   4557             return mTargetAlpha;
   4558         }
   4559 
   4560     }
   4561 
   4562     private void startSelectingText() {
   4563         mSelectingText = true;
   4564         mShowTextSelectionExtra = true;
   4565         animateHandles();
   4566     }
   4567 
   4568     private void animateHandle(boolean canShow, ObjectAnimator animator,
   4569             Point selectionPoint, int selectionLayerId,
   4570             SelectionHandleAlpha alpha) {
   4571         boolean isVisible = canShow && mSelectingText
   4572                 && ((mSelectionStarted && mSelectDraggingCursor == selectionPoint)
   4573                 || isHandleVisible(selectionPoint, selectionLayerId));
   4574         int targetValue = isVisible ? 255 : 0;
   4575         if (targetValue != alpha.getTargetAlpha()) {
   4576             alpha.setTargetAlpha(targetValue);
   4577             animator.setIntValues(targetValue);
   4578             animator.setDuration(SELECTION_HANDLE_ANIMATION_MS);
   4579             animator.start();
   4580         }
   4581     }
   4582 
   4583     private void animateHandles() {
   4584         boolean canShowBase = mSelectingText;
   4585         boolean canShowExtent = mSelectingText && !mIsCaretSelection;
   4586         animateHandle(canShowBase, mBaseHandleAlphaAnimator, mSelectCursorBase,
   4587                 mSelectCursorBaseLayerId, mBaseAlpha);
   4588         animateHandle(canShowExtent, mExtentHandleAlphaAnimator,
   4589                 mSelectCursorExtent, mSelectCursorExtentLayerId,
   4590                 mExtentAlpha);
   4591     }
   4592 
   4593     private void endSelectingText() {
   4594         mSelectingText = false;
   4595         mShowTextSelectionExtra = false;
   4596         animateHandles();
   4597     }
   4598 
   4599     private void ensureSelectionHandles() {
   4600         if (mSelectHandleCenter == null) {
   4601             mSelectHandleCenter = mContext.getResources().getDrawable(
   4602                     com.android.internal.R.drawable.text_select_handle_middle).mutate();
   4603             mSelectHandleLeft = mContext.getResources().getDrawable(
   4604                     com.android.internal.R.drawable.text_select_handle_left).mutate();
   4605             mSelectHandleRight = mContext.getResources().getDrawable(
   4606                     com.android.internal.R.drawable.text_select_handle_right).mutate();
   4607             // All handles have the same height, so we can save effort with
   4608             // this assumption.
   4609             mSelectOffset = new Point(0,
   4610                     -mSelectHandleLeft.getIntrinsicHeight());
   4611         }
   4612     }
   4613 
   4614     private void drawHandle(Point point, int handleId, Rect bounds,
   4615             int alpha, Canvas canvas) {
   4616         int offset;
   4617         int width;
   4618         int height;
   4619         Drawable drawable;
   4620         boolean isLeft = nativeIsHandleLeft(mNativeClass, handleId);
   4621         if (isLeft) {
   4622             drawable = mSelectHandleLeft;
   4623             width = mSelectHandleLeft.getIntrinsicWidth();
   4624             height = mSelectHandleLeft.getIntrinsicHeight();
   4625             // Magic formula copied from TextView
   4626             offset = (width * 3) / 4;
   4627         } else {
   4628             drawable = mSelectHandleRight;
   4629             width = mSelectHandleRight.getIntrinsicWidth();
   4630             height = mSelectHandleRight.getIntrinsicHeight();
   4631             // Magic formula copied from TextView
   4632             offset = width / 4;
   4633         }
   4634         int x = contentToViewDimension(point.x);
   4635         int y = contentToViewDimension(point.y);
   4636         bounds.set(x - offset, y, x - offset + width, y + height);
   4637         drawable.setBounds(bounds);
   4638         drawable.setAlpha(alpha);
   4639         drawable.draw(canvas);
   4640     }
   4641 
   4642     private void drawTextSelectionHandles(Canvas canvas) {
   4643         if (mBaseAlpha.getAlpha() == 0 && mExtentAlpha.getAlpha() == 0) {
   4644             return;
   4645         }
   4646         ensureSelectionHandles();
   4647         if (mIsCaretSelection) {
   4648             // Caret handle is centered
   4649             int x = contentToViewDimension(mSelectCursorBase.x) -
   4650                     (mSelectHandleCenter.getIntrinsicWidth() / 2);
   4651             int y = contentToViewDimension(mSelectCursorBase.y);
   4652             mSelectHandleBaseBounds.set(x, y,
   4653                     x + mSelectHandleCenter.getIntrinsicWidth(),
   4654                     y + mSelectHandleCenter.getIntrinsicHeight());
   4655             mSelectHandleCenter.setBounds(mSelectHandleBaseBounds);
   4656             mSelectHandleCenter.setAlpha(mBaseAlpha.getAlpha());
   4657             mSelectHandleCenter.draw(canvas);
   4658         } else {
   4659             drawHandle(mSelectCursorBase, HANDLE_ID_BASE,
   4660                     mSelectHandleBaseBounds, mBaseAlpha.getAlpha(), canvas);
   4661             drawHandle(mSelectCursorExtent, HANDLE_ID_EXTENT,
   4662                     mSelectHandleExtentBounds, mExtentAlpha.getAlpha(), canvas);
   4663         }
   4664     }
   4665 
   4666     private boolean isHandleVisible(Point selectionPoint, int layerId) {
   4667         boolean isVisible = true;
   4668         if (mIsEditingText) {
   4669             isVisible = mEditTextContentBounds.contains(selectionPoint.x,
   4670                     selectionPoint.y);
   4671         }
   4672         if (isVisible) {
   4673             isVisible = nativeIsPointVisible(mNativeClass, layerId,
   4674                     selectionPoint.x, selectionPoint.y);
   4675         }
   4676         return isVisible;
   4677     }
   4678 
   4679     /**
   4680      * Takes an int[4] array as an output param with the values being
   4681      * startX, startY, endX, endY
   4682      */
   4683     private void getSelectionHandles(int[] handles) {
   4684         handles[0] = mSelectCursorBase.x;
   4685         handles[1] = mSelectCursorBase.y;
   4686         handles[2] = mSelectCursorExtent.x;
   4687         handles[3] = mSelectCursorExtent.y;
   4688     }
   4689 
   4690     // draw history
   4691     private boolean mDrawHistory = false;
   4692     private Picture mHistoryPicture = null;
   4693     private int mHistoryWidth = 0;
   4694     private int mHistoryHeight = 0;
   4695 
   4696     // Only check the flag, can be called from WebCore thread
   4697     boolean drawHistory() {
   4698         return mDrawHistory;
   4699     }
   4700 
   4701     int getHistoryPictureWidth() {
   4702         return (mHistoryPicture != null) ? mHistoryPicture.getWidth() : 0;
   4703     }
   4704 
   4705     // Should only be called in UI thread
   4706     void switchOutDrawHistory() {
   4707         if (null == mWebViewCore) return; // CallbackProxy may trigger this
   4708         if (mDrawHistory && (getProgress() == 100 || nativeHasContent())) {
   4709             mDrawHistory = false;
   4710             mHistoryPicture = null;
   4711             invalidate();
   4712             int oldScrollX = getScrollX();
   4713             int oldScrollY = getScrollY();
   4714             setScrollXRaw(pinLocX(getScrollX()));
   4715             setScrollYRaw(pinLocY(getScrollY()));
   4716             if (oldScrollX != getScrollX() || oldScrollY != getScrollY()) {
   4717                 mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldScrollX, oldScrollY);
   4718             } else {
   4719                 sendOurVisibleRect();
   4720             }
   4721         }
   4722     }
   4723 
   4724     /**
   4725      *  Delete text from start to end in the focused textfield. If there is no
   4726      *  focus, or if start == end, silently fail.  If start and end are out of
   4727      *  order, swap them.
   4728      *  @param  start   Beginning of selection to delete.
   4729      *  @param  end     End of selection to delete.
   4730      */
   4731     /* package */ void deleteSelection(int start, int end) {
   4732         mTextGeneration++;
   4733         WebViewCore.TextSelectionData data
   4734                 = new WebViewCore.TextSelectionData(start, end, 0);
   4735         mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
   4736                 data);
   4737     }
   4738 
   4739     /**
   4740      *  Set the selection to (start, end) in the focused textfield. If start and
   4741      *  end are out of order, swap them.
   4742      *  @param  start   Beginning of selection.
   4743      *  @param  end     End of selection.
   4744      */
   4745     /* package */ void setSelection(int start, int end) {
   4746         if (mWebViewCore != null) {
   4747             mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
   4748         }
   4749     }
   4750 
   4751     @Override
   4752     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
   4753         if (mInputConnection == null) {
   4754             mInputConnection = new WebViewInputConnection();
   4755             mAutoCompletePopup = new AutoCompletePopup(this, mInputConnection);
   4756         }
   4757         mInputConnection.setupEditorInfo(outAttrs);
   4758         return mInputConnection;
   4759     }
   4760 
   4761     private void relocateAutoCompletePopup() {
   4762         if (mAutoCompletePopup != null) {
   4763             mAutoCompletePopup.resetRect();
   4764             mAutoCompletePopup.setText(mInputConnection.getEditable());
   4765         }
   4766     }
   4767 
   4768     /**
   4769      * Called in response to a message from webkit telling us that the soft
   4770      * keyboard should be launched.
   4771      */
   4772     private void displaySoftKeyboard(boolean isTextView) {
   4773         InputMethodManager imm = (InputMethodManager)
   4774                 mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
   4775 
   4776         // bring it back to the default level scale so that user can enter text
   4777         boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
   4778         if (zoom) {
   4779             mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
   4780             mZoomManager.setZoomScale(mZoomManager.getDefaultScale(), false);
   4781         }
   4782         // Used by plugins and contentEditable.
   4783         // Also used if the navigation cache is out of date, and
   4784         // does not recognize that a textfield is in focus.  In that
   4785         // case, use WebView as the targeted view.
   4786         // see http://b/issue?id=2457459
   4787         imm.showSoftInput(mWebView, 0);
   4788     }
   4789 
   4790     // Called by WebKit to instruct the UI to hide the keyboard
   4791     private void hideSoftKeyboard() {
   4792         InputMethodManager imm = InputMethodManager.peekInstance();
   4793         if (imm != null && (imm.isActive(mWebView))) {
   4794             imm.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
   4795         }
   4796     }
   4797 
   4798     /**
   4799      * Called by AutoCompletePopup to find saved form data associated with the
   4800      * textfield
   4801      * @param name Name of the textfield.
   4802      * @param nodePointer Pointer to the node of the textfield, so it can be
   4803      *          compared to the currently focused textfield when the data is
   4804      *          retrieved.
   4805      * @param autoFillable true if WebKit has determined this field is part of
   4806      *          a form that can be auto filled.
   4807      * @param autoComplete true if the attribute "autocomplete" is set to true
   4808      *          on the textfield.
   4809      */
   4810     /* package */ void requestFormData(String name, int nodePointer,
   4811             boolean autoFillable, boolean autoComplete) {
   4812         if (mWebViewCore.getSettings().getSaveFormData()) {
   4813             Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
   4814             update.arg1 = nodePointer;
   4815             RequestFormData updater = new RequestFormData(name, getUrl(),
   4816                     update, autoFillable, autoComplete);
   4817             Thread t = new Thread(updater);
   4818             t.start();
   4819         }
   4820     }
   4821 
   4822     /*
   4823      * This class requests an Adapter for the AutoCompletePopup which shows past
   4824      * entries stored in the database.  It is a Runnable so that it can be done
   4825      * in its own thread, without slowing down the UI.
   4826      */
   4827     private class RequestFormData implements Runnable {
   4828         private String mName;
   4829         private String mUrl;
   4830         private Message mUpdateMessage;
   4831         private boolean mAutoFillable;
   4832         private boolean mAutoComplete;
   4833         private WebSettingsClassic mWebSettings;
   4834 
   4835         public RequestFormData(String name, String url, Message msg,
   4836                 boolean autoFillable, boolean autoComplete) {
   4837             mName = name;
   4838             mUrl = WebTextView.urlForAutoCompleteData(url);
   4839             mUpdateMessage = msg;
   4840             mAutoFillable = autoFillable;
   4841             mAutoComplete = autoComplete;
   4842             mWebSettings = getSettings();
   4843         }
   4844 
   4845         @Override
   4846         public void run() {
   4847             ArrayList<String> pastEntries = new ArrayList<String>();
   4848 
   4849             if (mAutoFillable) {
   4850                 // Note that code inside the adapter click handler in AutoCompletePopup depends
   4851                 // on the AutoFill item being at the top of the drop down list. If you change
   4852                 // the order, make sure to do it there too!
   4853                 if (mWebSettings != null && mWebSettings.getAutoFillProfile() != null) {
   4854                     pastEntries.add(mWebView.getResources().getText(
   4855                             com.android.internal.R.string.autofill_this_form).toString() +
   4856                             " " +
   4857                     mAutoFillData.getPreviewString());
   4858                     mAutoCompletePopup.setIsAutoFillProfileSet(true);
   4859                 } else {
   4860                     // There is no autofill profile set up yet, so add an option that
   4861                     // will invite the user to set their profile up.
   4862                     pastEntries.add(mWebView.getResources().getText(
   4863                             com.android.internal.R.string.setup_autofill).toString());
   4864                     mAutoCompletePopup.setIsAutoFillProfileSet(false);
   4865                 }
   4866             }
   4867 
   4868             if (mAutoComplete) {
   4869                 pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
   4870             }
   4871 
   4872             if (pastEntries.size() > 0) {
   4873                 ArrayAdapter<String> adapter = new ArrayAdapter<String>(
   4874                         mContext,
   4875                         com.android.internal.R.layout.web_text_view_dropdown,
   4876                         pastEntries);
   4877                 mUpdateMessage.obj = adapter;
   4878                 mUpdateMessage.sendToTarget();
   4879             }
   4880         }
   4881     }
   4882 
   4883     /**
   4884      * Dump the display tree to "/sdcard/displayTree.txt"
   4885      *
   4886      * debug only
   4887      */
   4888     public void dumpDisplayTree() {
   4889         nativeDumpDisplayTree(getUrl());
   4890     }
   4891 
   4892     /**
   4893      * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
   4894      * "/sdcard/domTree.txt"
   4895      *
   4896      * debug only
   4897      */
   4898     public void dumpDomTree(boolean toFile) {
   4899         mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
   4900     }
   4901 
   4902     /**
   4903      * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
   4904      * to "/sdcard/renderTree.txt"
   4905      *
   4906      * debug only
   4907      */
   4908     public void dumpRenderTree(boolean toFile) {
   4909         mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
   4910     }
   4911 
   4912     /**
   4913      * Called by DRT on UI thread, need to proxy to WebCore thread.
   4914      *
   4915      * debug only
   4916      */
   4917     public void setUseMockDeviceOrientation() {
   4918         mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_DEVICE_ORIENTATION);
   4919     }
   4920 
   4921     /**
   4922      * Sets use of the Geolocation mock client. Also resets that client. Called
   4923      * by DRT on UI thread, need to proxy to WebCore thread.
   4924      *
   4925      * debug only
   4926      */
   4927     public void setUseMockGeolocation() {
   4928         mWebViewCore.sendMessage(EventHub.SET_USE_MOCK_GEOLOCATION);
   4929     }
   4930 
   4931     /**
   4932      * Called by DRT on WebCore thread.
   4933      *
   4934      * debug only
   4935      */
   4936     public void setMockGeolocationPosition(double latitude, double longitude, double accuracy) {
   4937         mWebViewCore.setMockGeolocationPosition(latitude, longitude, accuracy);
   4938     }
   4939 
   4940     /**
   4941      * Called by DRT on WebCore thread.
   4942      *
   4943      * debug only
   4944      */
   4945     public void setMockGeolocationError(int code, String message) {
   4946         mWebViewCore.setMockGeolocationError(code, message);
   4947     }
   4948 
   4949     /**
   4950      * Called by DRT on WebCore thread.
   4951      *
   4952      * debug only
   4953      */
   4954     public void setMockGeolocationPermission(boolean allow) {
   4955         mWebViewCore.setMockGeolocationPermission(allow);
   4956     }
   4957 
   4958     /**
   4959      * Called by DRT on WebCore thread.
   4960      *
   4961      * debug only
   4962      */
   4963     public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
   4964             boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
   4965         mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
   4966                 canProvideGamma, gamma);
   4967     }
   4968 
   4969     // This is used to determine long press with the center key.  Does not
   4970     // affect long press with the trackball/touch.
   4971     private boolean mGotCenterDown = false;
   4972 
   4973     @Override
   4974     public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
   4975         if (mBlockWebkitViewMessages) {
   4976             return false;
   4977         }
   4978         // send complex characters to webkit for use by JS and plugins
   4979         if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getCharacters() != null) {
   4980             // pass the key to DOM
   4981             sendBatchableInputMessage(EventHub.KEY_DOWN, 0, 0, event);
   4982             sendBatchableInputMessage(EventHub.KEY_UP, 0, 0, event);
   4983             // return true as DOM handles the key
   4984             return true;
   4985         }
   4986         return false;
   4987     }
   4988 
   4989     private boolean isEnterActionKey(int keyCode) {
   4990         return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
   4991                 || keyCode == KeyEvent.KEYCODE_ENTER
   4992                 || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
   4993     }
   4994 
   4995     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
   4996         if (mAutoCompletePopup != null) {
   4997             return mAutoCompletePopup.onKeyPreIme(keyCode, event);
   4998         }
   4999         return false;
   5000     }
   5001 
   5002     @Override
   5003     public boolean onKeyDown(int keyCode, KeyEvent event) {
   5004         if (DebugFlags.WEB_VIEW) {
   5005             Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
   5006                     + "keyCode=" + keyCode
   5007                     + ", " + event + ", unicode=" + event.getUnicodeChar());
   5008         }
   5009         if (mIsCaretSelection) {
   5010             selectionDone();
   5011         }
   5012         if (mBlockWebkitViewMessages) {
   5013             return false;
   5014         }
   5015 
   5016         // don't implement accelerator keys here; defer to host application
   5017         if (event.isCtrlPressed()) {
   5018             return false;
   5019         }
   5020 
   5021         if (mNativeClass == 0) {
   5022             return false;
   5023         }
   5024 
   5025         // do this hack up front, so it always works, regardless of touch-mode
   5026         if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
   5027             mAutoRedraw = !mAutoRedraw;
   5028             if (mAutoRedraw) {
   5029                 invalidate();
   5030             }
   5031             return true;
   5032         }
   5033 
   5034         // Bubble up the key event if
   5035         // 1. it is a system key; or
   5036         // 2. the host application wants to handle it;
   5037         if (event.isSystem()
   5038                 || mCallbackProxy.uiOverrideKeyEvent(event)) {
   5039             return false;
   5040         }
   5041 
   5042         // See if the accessibility injector needs to handle this event.
   5043         if (isAccessibilityInjectionEnabled()
   5044                 && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
   5045             return true;
   5046         }
   5047 
   5048         if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
   5049             if (event.hasNoModifiers()) {
   5050                 pageUp(false);
   5051                 return true;
   5052             } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
   5053                 pageUp(true);
   5054                 return true;
   5055             }
   5056         }
   5057 
   5058         if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
   5059             if (event.hasNoModifiers()) {
   5060                 pageDown(false);
   5061                 return true;
   5062             } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
   5063                 pageDown(true);
   5064                 return true;
   5065             }
   5066         }
   5067 
   5068         if (keyCode == KeyEvent.KEYCODE_MOVE_HOME && event.hasNoModifiers()) {
   5069             pageUp(true);
   5070             return true;
   5071         }
   5072 
   5073         if (keyCode == KeyEvent.KEYCODE_MOVE_END && event.hasNoModifiers()) {
   5074             pageDown(true);
   5075             return true;
   5076         }
   5077 
   5078         if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
   5079                 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
   5080             switchOutDrawHistory();
   5081         }
   5082 
   5083         if (isEnterActionKey(keyCode)) {
   5084             switchOutDrawHistory();
   5085             if (event.getRepeatCount() == 0) {
   5086                 if (mSelectingText) {
   5087                     return true; // discard press if copy in progress
   5088                 }
   5089                 mGotCenterDown = true;
   5090                 mPrivateHandler.sendMessageDelayed(mPrivateHandler
   5091                         .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
   5092             }
   5093         }
   5094 
   5095         if (getSettings().getNavDump()) {
   5096             switch (keyCode) {
   5097                 case KeyEvent.KEYCODE_4:
   5098                     dumpDisplayTree();
   5099                     break;
   5100                 case KeyEvent.KEYCODE_5:
   5101                 case KeyEvent.KEYCODE_6:
   5102                     dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
   5103                     break;
   5104                 case KeyEvent.KEYCODE_7:
   5105                 case KeyEvent.KEYCODE_8:
   5106                     dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
   5107                     break;
   5108             }
   5109         }
   5110 
   5111         // pass the key to DOM
   5112         sendKeyEvent(event);
   5113         // return true as DOM handles the key
   5114         return true;
   5115     }
   5116 
   5117     @Override
   5118     public boolean onKeyUp(int keyCode, KeyEvent event) {
   5119         if (DebugFlags.WEB_VIEW) {
   5120             Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
   5121                     + ", " + event + ", unicode=" + event.getUnicodeChar());
   5122         }
   5123         if (mBlockWebkitViewMessages) {
   5124             return false;
   5125         }
   5126 
   5127         if (mNativeClass == 0) {
   5128             return false;
   5129         }
   5130 
   5131         // special CALL handling when cursor node's href is "tel:XXX"
   5132         if (keyCode == KeyEvent.KEYCODE_CALL
   5133                 && mInitialHitTestResult != null
   5134                 && mInitialHitTestResult.getType() == HitTestResult.PHONE_TYPE) {
   5135             String text = mInitialHitTestResult.getExtra();
   5136             Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
   5137             mContext.startActivity(intent);
   5138             return true;
   5139         }
   5140 
   5141         // Bubble up the key event if
   5142         // 1. it is a system key; or
   5143         // 2. the host application wants to handle it;
   5144         if (event.isSystem()
   5145                 || mCallbackProxy.uiOverrideKeyEvent(event)) {
   5146             return false;
   5147         }
   5148 
   5149         // See if the accessibility injector needs to handle this event.
   5150         if (isAccessibilityInjectionEnabled()
   5151                 && getAccessibilityInjector().handleKeyEventIfNecessary(event)) {
   5152             return true;
   5153         }
   5154 
   5155         if (isEnterActionKey(keyCode)) {
   5156             // remove the long press message first
   5157             mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
   5158             mGotCenterDown = false;
   5159 
   5160             if (mSelectingText) {
   5161                 copySelection();
   5162                 selectionDone();
   5163                 return true; // discard press if copy in progress
   5164             }
   5165         }
   5166 
   5167         // pass the key to DOM
   5168         sendKeyEvent(event);
   5169         // return true as DOM handles the key
   5170         return true;
   5171     }
   5172 
   5173     private boolean startSelectActionMode() {
   5174         mSelectCallback = new SelectActionModeCallback();
   5175         mSelectCallback.setTextSelected(!mIsCaretSelection);
   5176         mSelectCallback.setWebView(this);
   5177         if (mWebView.startActionMode(mSelectCallback) == null) {
   5178             // There is no ActionMode, so do not allow the user to modify a
   5179             // selection.
   5180             selectionDone();
   5181             return false;
   5182         }
   5183         mWebView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
   5184         return true;
   5185     }
   5186 
   5187     private void showPasteWindow() {
   5188         ClipboardManager cm = (ClipboardManager)(mContext
   5189                 .getSystemService(Context.CLIPBOARD_SERVICE));
   5190         if (cm.hasPrimaryClip()) {
   5191             Point cursorPoint = new Point(contentToViewX(mSelectCursorBase.x),
   5192                     contentToViewY(mSelectCursorBase.y));
   5193             Point cursorTop = calculateBaseCaretTop();
   5194             cursorTop.set(contentToViewX(cursorTop.x),
   5195                     contentToViewY(cursorTop.y));
   5196 
   5197             int[] location = new int[2];
   5198             mWebView.getLocationInWindow(location);
   5199             int offsetX = location[0] - getScrollX();
   5200             int offsetY = location[1] - getScrollY();
   5201             cursorPoint.offset(offsetX, offsetY);
   5202             cursorTop.offset(offsetX, offsetY);
   5203             if (mPasteWindow == null) {
   5204                 mPasteWindow = new PastePopupWindow();
   5205             }
   5206             mPasteWindow.show(cursorPoint, cursorTop, location[0], location[1]);
   5207         }
   5208     }
   5209 
   5210     /**
   5211      * Given segment AB, this finds the point C along AB that is closest to
   5212      * point and then returns it scale along AB. The scale factor is AC/AB.
   5213      *
   5214      * @param x The x coordinate of the point near segment AB that determines
   5215      * the scale factor.
   5216      * @param y The y coordinate of the point near segment AB that determines
   5217      * the scale factor.
   5218      * @param a The first point of the line segment.
   5219      * @param b The second point of the line segment.
   5220      * @return The scale factor AC/AB, where C is the point on AB closest to
   5221      *         point.
   5222      */
   5223     private static float scaleAlongSegment(int x, int y, PointF a, PointF b) {
   5224         // The bottom line of the text box is line AB
   5225         float abX = b.x - a.x;
   5226         float abY = b.y - a.y;
   5227         float ab2 = (abX * abX) + (abY * abY);
   5228 
   5229         // The line from first point in text bounds to bottom is AP
   5230         float apX = x - a.x;
   5231         float apY = y - a.y;
   5232         float abDotAP = (apX * abX) + (apY * abY);
   5233         float scale = abDotAP / ab2;
   5234         return scale;
   5235     }
   5236 
   5237     private Point calculateBaseCaretTop() {
   5238         return calculateCaretTop(mSelectCursorBase, mSelectCursorBaseTextQuad);
   5239     }
   5240 
   5241     private Point calculateDraggingCaretTop() {
   5242         return calculateCaretTop(mSelectDraggingCursor, mSelectDraggingTextQuad);
   5243     }
   5244 
   5245     /**
   5246      * Assuming arbitrary shape of a quadralateral forming text bounds, this
   5247      * calculates the top of a caret.
   5248      */
   5249     private static Point calculateCaretTop(Point base, QuadF quad) {
   5250         float scale = scaleAlongSegment(base.x, base.y, quad.p4, quad.p3);
   5251         int x = Math.round(scaleCoordinate(scale, quad.p1.x, quad.p2.x));
   5252         int y = Math.round(scaleCoordinate(scale, quad.p1.y, quad.p2.y));
   5253         return new Point(x, y);
   5254     }
   5255 
   5256     private void hidePasteButton() {
   5257         if (mPasteWindow != null) {
   5258             mPasteWindow.hide();
   5259         }
   5260     }
   5261 
   5262     private void syncSelectionCursors() {
   5263         mSelectCursorBaseLayerId =
   5264                 nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE,
   5265                         mSelectCursorBase, mSelectCursorBaseTextQuad);
   5266         mSelectCursorExtentLayerId =
   5267                 nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT,
   5268                         mSelectCursorExtent, mSelectCursorExtentTextQuad);
   5269     }
   5270 
   5271     private boolean setupWebkitSelect() {
   5272         syncSelectionCursors();
   5273         if (!mIsCaretSelection && !startSelectActionMode()) {
   5274             selectionDone();
   5275             return false;
   5276         }
   5277         startSelectingText();
   5278         mTouchMode = TOUCH_DRAG_MODE;
   5279         return true;
   5280     }
   5281 
   5282     private void updateWebkitSelection(boolean isSnapped) {
   5283         int handleId = (mSelectDraggingCursor == mSelectCursorBase)
   5284                 ? HANDLE_ID_BASE : HANDLE_ID_EXTENT;
   5285         int x = mSelectDraggingCursor.x;
   5286         int y = mSelectDraggingCursor.y;
   5287         if (isSnapped) {
   5288             // "center" the cursor in the snapping quad
   5289             Point top = calculateDraggingCaretTop();
   5290             x = Math.round((top.x + x) / 2);
   5291             y = Math.round((top.y + y) / 2);
   5292         }
   5293         mWebViewCore.removeMessages(EventHub.SELECT_TEXT);
   5294         mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT,
   5295                 x, y, (Integer)handleId);
   5296     }
   5297 
   5298     private void resetCaretTimer() {
   5299         mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
   5300         if (!mSelectionStarted) {
   5301             mPrivateHandler.sendEmptyMessageDelayed(CLEAR_CARET_HANDLE,
   5302                     CARET_HANDLE_STAMINA_MS);
   5303         }
   5304     }
   5305 
   5306     /**
   5307      * Select all of the text in this WebView.
   5308      *
   5309      * This is an implementation detail.
   5310      */
   5311     public void selectAll() {
   5312         mWebViewCore.sendMessage(EventHub.SELECT_ALL);
   5313     }
   5314 
   5315     /**
   5316      * Called when the selection has been removed.
   5317      */
   5318     void selectionDone() {
   5319         if (mSelectingText) {
   5320             hidePasteButton();
   5321             endSelectingText();
   5322             // finish is idempotent, so this is fine even if selectionDone was
   5323             // called by mSelectCallback.onDestroyActionMode
   5324             if (mSelectCallback != null) {
   5325                 mSelectCallback.finish();
   5326                 mSelectCallback = null;
   5327             }
   5328             invalidate(); // redraw without selection
   5329             mAutoScrollX = 0;
   5330             mAutoScrollY = 0;
   5331             mSentAutoScrollMessage = false;
   5332         }
   5333     }
   5334 
   5335     /**
   5336      * Copy the selection to the clipboard
   5337      *
   5338      * This is an implementation detail.
   5339      */
   5340     public boolean copySelection() {
   5341         boolean copiedSomething = false;
   5342         String selection = getSelection();
   5343         if (selection != null && selection != "") {
   5344             if (DebugFlags.WEB_VIEW) {
   5345                 Log.v(LOGTAG, "copySelection \"" + selection + "\"");
   5346             }
   5347             Toast.makeText(mContext
   5348                     , com.android.internal.R.string.text_copied
   5349                     , Toast.LENGTH_SHORT).show();
   5350             copiedSomething = true;
   5351             ClipboardManager cm = (ClipboardManager)mContext
   5352                     .getSystemService(Context.CLIPBOARD_SERVICE);
   5353             cm.setText(selection);
   5354             int[] handles = new int[4];
   5355             getSelectionHandles(handles);
   5356             mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles);
   5357         }
   5358         invalidate(); // remove selection region and pointer
   5359         return copiedSomething;
   5360     }
   5361 
   5362     /**
   5363      * Cut the selected text into the clipboard
   5364      *
   5365      * This is an implementation detail
   5366      */
   5367     public void cutSelection() {
   5368         copySelection();
   5369         int[] handles = new int[4];
   5370         getSelectionHandles(handles);
   5371         mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
   5372     }
   5373 
   5374     /**
   5375      * Paste text from the clipboard to the cursor position.
   5376      *
   5377      * This is an implementation detail
   5378      */
   5379     public void pasteFromClipboard() {
   5380         ClipboardManager cm = (ClipboardManager)mContext
   5381                 .getSystemService(Context.CLIPBOARD_SERVICE);
   5382         ClipData clipData = cm.getPrimaryClip();
   5383         if (clipData != null) {
   5384             ClipData.Item clipItem = clipData.getItemAt(0);
   5385             CharSequence pasteText = clipItem.getText();
   5386             if (mInputConnection != null) {
   5387                 mInputConnection.replaceSelection(pasteText);
   5388             }
   5389         }
   5390     }
   5391 
   5392     /**
   5393      * Returns the currently highlighted text as a string.
   5394      */
   5395     String getSelection() {
   5396         if (mNativeClass == 0) return "";
   5397         return nativeGetSelection();
   5398     }
   5399 
   5400     @Override
   5401     public void onAttachedToWindow() {
   5402         if (mWebView.hasWindowFocus()) setActive(true);
   5403 
   5404         if (isAccessibilityInjectionEnabled()) {
   5405             getAccessibilityInjector().toggleAccessibilityFeedback(true);
   5406         }
   5407 
   5408         updateHwAccelerated();
   5409     }
   5410 
   5411     @Override
   5412     public void onDetachedFromWindow() {
   5413         clearHelpers();
   5414         mZoomManager.dismissZoomPicker();
   5415         if (mWebView.hasWindowFocus()) setActive(false);
   5416 
   5417         if (isAccessibilityInjectionEnabled()) {
   5418             getAccessibilityInjector().toggleAccessibilityFeedback(false);
   5419         }
   5420 
   5421         updateHwAccelerated();
   5422 
   5423         ensureFunctorDetached();
   5424     }
   5425 
   5426     @Override
   5427     public void onVisibilityChanged(View changedView, int visibility) {
   5428         // The zoomManager may be null if the webview is created from XML that
   5429         // specifies the view's visibility param as not visible (see http://b/2794841)
   5430         if (visibility != View.VISIBLE && mZoomManager != null) {
   5431             mZoomManager.dismissZoomPicker();
   5432         }
   5433         updateDrawingState();
   5434     }
   5435 
   5436     void setActive(boolean active) {
   5437         if (active) {
   5438             if (mWebView.hasFocus()) {
   5439                 // If our window regained focus, and we have focus, then begin
   5440                 // drawing the cursor ring
   5441                 mDrawCursorRing = true;
   5442                 setFocusControllerActive(true);
   5443             } else {
   5444                 mDrawCursorRing = false;
   5445                 setFocusControllerActive(false);
   5446             }
   5447         } else {
   5448             if (!mZoomManager.isZoomPickerVisible()) {
   5449                 /*
   5450                  * The external zoom controls come in their own window, so our
   5451                  * window loses focus. Our policy is to not draw the cursor ring
   5452                  * if our window is not focused, but this is an exception since
   5453                  * the user can still navigate the web page with the zoom
   5454                  * controls showing.
   5455                  */
   5456                 mDrawCursorRing = false;
   5457             }
   5458             mKeysPressed.clear();
   5459             mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
   5460             mTouchMode = TOUCH_DONE_MODE;
   5461             setFocusControllerActive(false);
   5462         }
   5463         invalidate();
   5464     }
   5465 
   5466     // To avoid drawing the cursor ring, and remove the TextView when our window
   5467     // loses focus.
   5468     @Override
   5469     public void onWindowFocusChanged(boolean hasWindowFocus) {
   5470         setActive(hasWindowFocus);
   5471         if (hasWindowFocus) {
   5472             JWebCoreJavaBridge.setActiveWebView(this);
   5473             if (mPictureUpdatePausedForFocusChange) {
   5474                 WebViewCore.resumeUpdatePicture(mWebViewCore);
   5475                 mPictureUpdatePausedForFocusChange = false;
   5476             }
   5477         } else {
   5478             JWebCoreJavaBridge.removeActiveWebView(this);
   5479             final WebSettings settings = getSettings();
   5480             if (settings != null && settings.enableSmoothTransition() &&
   5481                     mWebViewCore != null && !WebViewCore.isUpdatePicturePaused(mWebViewCore)) {
   5482                 WebViewCore.pauseUpdatePicture(mWebViewCore);
   5483                 mPictureUpdatePausedForFocusChange = true;
   5484             }
   5485         }
   5486     }
   5487 
   5488     /*
   5489      * Pass a message to WebCore Thread, telling the WebCore::Page's
   5490      * FocusController to be  "inactive" so that it will
   5491      * not draw the blinking cursor.  It gets set to "active" to draw the cursor
   5492      * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
   5493      */
   5494     /* package */ void setFocusControllerActive(boolean active) {
   5495         if (mWebViewCore == null) return;
   5496         mWebViewCore.sendMessage(EventHub.SET_ACTIVE, active ? 1 : 0, 0);
   5497         // Need to send this message after the document regains focus.
   5498         if (active && mListBoxMessage != null) {
   5499             mWebViewCore.sendMessage(mListBoxMessage);
   5500             mListBoxMessage = null;
   5501         }
   5502     }
   5503 
   5504     @Override
   5505     public void onFocusChanged(boolean focused, int direction,
   5506             Rect previouslyFocusedRect) {
   5507         if (DebugFlags.WEB_VIEW) {
   5508             Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
   5509         }
   5510         if (focused) {
   5511             mDrawCursorRing = true;
   5512             setFocusControllerActive(true);
   5513         } else {
   5514             mDrawCursorRing = false;
   5515             setFocusControllerActive(false);
   5516             mKeysPressed.clear();
   5517         }
   5518         if (!mTouchHighlightRegion.isEmpty()) {
   5519             mWebView.invalidate(mTouchHighlightRegion.getBounds());
   5520         }
   5521     }
   5522 
   5523     // updateRectsForGL() happens almost every draw call, in order to avoid creating
   5524     // any object in this code path, we move the local variable out to be a private
   5525     // final member, and we marked them as mTemp*.
   5526     private final Point mTempVisibleRectOffset = new Point();
   5527     private final Rect mTempVisibleRect = new Rect();
   5528 
   5529     void updateRectsForGL() {
   5530         // Use the getGlobalVisibleRect() to get the intersection among the parents
   5531         // visible == false means we're clipped - send a null rect down to indicate that
   5532         // we should not draw
   5533         boolean visible = mWebView.getGlobalVisibleRect(mTempVisibleRect, mTempVisibleRectOffset);
   5534         mInvScreenRect.set(mTempVisibleRect);
   5535         if (visible) {
   5536             // Then need to invert the Y axis, just for GL
   5537             View rootView = mWebView.getRootView();
   5538             int rootViewHeight = rootView.getHeight();
   5539             mScreenRect.set(mInvScreenRect);
   5540             int savedWebViewBottom = mInvScreenRect.bottom;
   5541             mInvScreenRect.bottom = rootViewHeight - mInvScreenRect.top - getVisibleTitleHeightImpl();
   5542             mInvScreenRect.top = rootViewHeight - savedWebViewBottom;
   5543             mIsWebViewVisible = true;
   5544         } else {
   5545             mIsWebViewVisible = false;
   5546         }
   5547 
   5548         mTempVisibleRect.offset(-mTempVisibleRectOffset.x, -mTempVisibleRectOffset.y);
   5549         viewToContentVisibleRect(mVisibleContentRect, mTempVisibleRect);
   5550 
   5551         nativeUpdateDrawGLFunction(mNativeClass, mIsWebViewVisible ? mInvScreenRect : null,
   5552                 mIsWebViewVisible ? mScreenRect : null,
   5553                 mVisibleContentRect, getScale());
   5554     }
   5555 
   5556     // Input : viewRect, rect in view/screen coordinate.
   5557     // Output: contentRect, rect in content/document coordinate.
   5558     private void viewToContentVisibleRect(RectF contentRect, Rect viewRect) {
   5559         contentRect.left = viewToContentXf(viewRect.left) / mWebView.getScaleX();
   5560         // viewToContentY will remove the total height of the title bar.  Add
   5561         // the visible height back in to account for the fact that if the title
   5562         // bar is partially visible, the part of the visible rect which is
   5563         // displaying our content is displaced by that amount.
   5564         contentRect.top = viewToContentYf(viewRect.top + getVisibleTitleHeightImpl())
   5565                 / mWebView.getScaleY();
   5566         contentRect.right = viewToContentXf(viewRect.right) / mWebView.getScaleX();
   5567         contentRect.bottom = viewToContentYf(viewRect.bottom) / mWebView.getScaleY();
   5568     }
   5569 
   5570     @Override
   5571     public boolean setFrame(int left, int top, int right, int bottom) {
   5572         boolean changed = mWebViewPrivate.super_setFrame(left, top, right, bottom);
   5573         if (!changed && mHeightCanMeasure) {
   5574             // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
   5575             // in WebViewCore after we get the first layout. We do call
   5576             // requestLayout() when we get contentSizeChanged(). But the View
   5577             // system won't call onSizeChanged if the dimension is not changed.
   5578             // In this case, we need to call sendViewSizeZoom() explicitly to
   5579             // notify the WebKit about the new dimensions.
   5580             sendViewSizeZoom(false);
   5581         }
   5582         updateRectsForGL();
   5583         return changed;
   5584     }
   5585 
   5586     @Override
   5587     public void onSizeChanged(int w, int h, int ow, int oh) {
   5588         // adjust the max viewport width depending on the view dimensions. This
   5589         // is to ensure the scaling is not going insane. So do not shrink it if
   5590         // the view size is temporarily smaller, e.g. when soft keyboard is up.
   5591         int newMaxViewportWidth = (int) (Math.max(w, h) / mZoomManager.getDefaultMinZoomScale());
   5592         if (newMaxViewportWidth > sMaxViewportWidth) {
   5593             sMaxViewportWidth = newMaxViewportWidth;
   5594         }
   5595 
   5596         mZoomManager.onSizeChanged(w, h, ow, oh);
   5597 
   5598         if (mLoadedPicture != null && mDelaySetPicture == null) {
   5599             // Size changes normally result in a new picture
   5600             // Re-set the loaded picture to simulate that
   5601             // However, do not update the base layer as that hasn't changed
   5602             setNewPicture(mLoadedPicture, false);
   5603         }
   5604         if (mIsEditingText) {
   5605             scrollEditIntoView();
   5606         }
   5607         relocateAutoCompletePopup();
   5608     }
   5609 
   5610     /**
   5611      * Scrolls the edit field into view using the minimum scrolling necessary.
   5612      * If the edit field is too large to fit in the visible window, the caret
   5613      * dimensions are used so that at least the caret is visible.
   5614      * A buffer of EDIT_RECT_BUFFER in view pixels is used to offset the
   5615      * edit rectangle to ensure a margin with the edge of the screen.
   5616      */
   5617     private void scrollEditIntoView() {
   5618         Rect visibleRect = new Rect(viewToContentX(getScrollX()),
   5619                 viewToContentY(getScrollY()),
   5620                 viewToContentX(getScrollX() + getWidth()),
   5621                 viewToContentY(getScrollY() + getViewHeightWithTitle()));
   5622         if (visibleRect.contains(mEditTextContentBounds)) {
   5623             return; // no need to scroll
   5624         }
   5625         syncSelectionCursors();
   5626         nativeFindMaxVisibleRect(mNativeClass, mEditTextLayerId, visibleRect);
   5627         final int buffer = Math.max(1, viewToContentDimension(EDIT_RECT_BUFFER));
   5628         Rect showRect = new Rect(
   5629                 Math.max(0, mEditTextContentBounds.left - buffer),
   5630                 Math.max(0, mEditTextContentBounds.top - buffer),
   5631                 mEditTextContentBounds.right + buffer,
   5632                 mEditTextContentBounds.bottom + buffer);
   5633         Point caretTop = calculateBaseCaretTop();
   5634         if (visibleRect.width() < mEditTextContentBounds.width()) {
   5635             // The whole edit won't fit in the width, so use the caret rect
   5636             if (mSelectCursorBase.x < caretTop.x) {
   5637                 showRect.left = Math.max(0, mSelectCursorBase.x - buffer);
   5638                 showRect.right = caretTop.x + buffer;
   5639             } else {
   5640                 showRect.left = Math.max(0, caretTop.x - buffer);
   5641                 showRect.right = mSelectCursorBase.x + buffer;
   5642             }
   5643         }
   5644         if (visibleRect.height() < mEditTextContentBounds.height()) {
   5645             // The whole edit won't fit in the height, so use the caret rect
   5646             if (mSelectCursorBase.y > caretTop.y) {
   5647                 showRect.top = Math.max(0, caretTop.y - buffer);
   5648                 showRect.bottom = mSelectCursorBase.y + buffer;
   5649             } else {
   5650                 showRect.top = Math.max(0, mSelectCursorBase.y - buffer);
   5651                 showRect.bottom = caretTop.y + buffer;
   5652             }
   5653         }
   5654 
   5655         if (visibleRect.contains(showRect)) {
   5656             return; // no need to scroll
   5657         }
   5658 
   5659         int scrollX = viewToContentX(getScrollX());
   5660         if (visibleRect.left > showRect.left) {
   5661             // We are scrolled too far
   5662             scrollX += showRect.left - visibleRect.left;
   5663         } else if (visibleRect.right < showRect.right) {
   5664             // We aren't scrolled enough to include the right
   5665             scrollX += showRect.right - visibleRect.right;
   5666         }
   5667         int scrollY = viewToContentY(getScrollY());
   5668         if (visibleRect.top > showRect.top) {
   5669             scrollY += showRect.top - visibleRect.top;
   5670         } else if (visibleRect.bottom < showRect.bottom) {
   5671             scrollY += showRect.bottom - visibleRect.bottom;
   5672         }
   5673 
   5674         contentScrollTo(scrollX, scrollY, false);
   5675     }
   5676 
   5677     @Override
   5678     public void onScrollChanged(int l, int t, int oldl, int oldt) {
   5679         if (!mInOverScrollMode) {
   5680             sendOurVisibleRect();
   5681             // update WebKit if visible title bar height changed. The logic is same
   5682             // as getVisibleTitleHeightImpl.
   5683             int titleHeight = getTitleHeight();
   5684             if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
   5685                 sendViewSizeZoom(false);
   5686             }
   5687         }
   5688     }
   5689 
   5690     @Override
   5691     public boolean dispatchKeyEvent(KeyEvent event) {
   5692         switch (event.getAction()) {
   5693             case KeyEvent.ACTION_DOWN:
   5694                 mKeysPressed.add(Integer.valueOf(event.getKeyCode()));
   5695                 break;
   5696             case KeyEvent.ACTION_MULTIPLE:
   5697                 // Always accept the action.
   5698                 break;
   5699             case KeyEvent.ACTION_UP:
   5700                 int location = mKeysPressed.indexOf(Integer.valueOf(event.getKeyCode()));
   5701                 if (location == -1) {
   5702                     // We did not receive the key down for this key, so do not
   5703                     // handle the key up.
   5704                     return false;
   5705                 } else {
   5706                     // We did receive the key down.  Handle the key up, and
   5707                     // remove it from our pressed keys.
   5708                     mKeysPressed.remove(location);
   5709                 }
   5710                 break;
   5711             default:
   5712                 // Accept the action.  This should not happen, unless a new
   5713                 // action is added to KeyEvent.
   5714                 break;
   5715         }
   5716         return mWebViewPrivate.super_dispatchKeyEvent(event);
   5717     }
   5718 
   5719     /*
   5720      * Here is the snap align logic:
   5721      * 1. If it starts nearly horizontally or vertically, snap align;
   5722      * 2. If there is a dramitic direction change, let it go;
   5723      *
   5724      * Adjustable parameters. Angle is the radians on a unit circle, limited
   5725      * to quadrant 1. Values range from 0f (horizontal) to PI/2 (vertical)
   5726      */
   5727     private static final float HSLOPE_TO_START_SNAP = .25f;
   5728     private static final float HSLOPE_TO_BREAK_SNAP = .4f;
   5729     private static final float VSLOPE_TO_START_SNAP = 1.25f;
   5730     private static final float VSLOPE_TO_BREAK_SNAP = .95f;
   5731     /*
   5732      *  These values are used to influence the average angle when entering
   5733      *  snap mode. If is is the first movement entering snap, we set the average
   5734      *  to the appropriate ideal. If the user is entering into snap after the
   5735      *  first movement, then we average the average angle with these values.
   5736      */
   5737     private static final float ANGLE_VERT = 2f;
   5738     private static final float ANGLE_HORIZ = 0f;
   5739     /*
   5740      *  The modified moving average weight.
   5741      *  Formula: MAV[t]=MAV[t-1] + (P[t]-MAV[t-1])/n
   5742      */
   5743     private static final float MMA_WEIGHT_N = 5;
   5744 
   5745     private boolean inFullScreenMode() {
   5746         return mFullScreenHolder != null;
   5747     }
   5748 
   5749     private void dismissFullScreenMode() {
   5750         if (inFullScreenMode()) {
   5751             mFullScreenHolder.hide();
   5752             mFullScreenHolder = null;
   5753             invalidate();
   5754         }
   5755     }
   5756 
   5757     void onPinchToZoomAnimationStart() {
   5758         // cancel the single touch handling
   5759         cancelTouch();
   5760         onZoomAnimationStart();
   5761     }
   5762 
   5763     void onPinchToZoomAnimationEnd(ScaleGestureDetector detector) {
   5764         onZoomAnimationEnd();
   5765         // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as
   5766         // it may trigger the unwanted click, can't use TOUCH_DRAG_MODE
   5767         // as it may trigger the unwanted fling.
   5768         mTouchMode = TOUCH_PINCH_DRAG;
   5769         mConfirmMove = true;
   5770         startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
   5771     }
   5772 
   5773     // See if there is a layer at x, y and switch to TOUCH_DRAG_LAYER_MODE if a
   5774     // layer is found.
   5775     private void startScrollingLayer(float x, float y) {
   5776         if (mNativeClass == 0)
   5777             return;
   5778 
   5779         int contentX = viewToContentX((int) x + getScrollX());
   5780         int contentY = viewToContentY((int) y + getScrollY());
   5781         mCurrentScrollingLayerId = nativeScrollableLayer(mNativeClass,
   5782                 contentX, contentY, mScrollingLayerRect, mScrollingLayerBounds);
   5783         if (mCurrentScrollingLayerId != 0) {
   5784             mTouchMode = TOUCH_DRAG_LAYER_MODE;
   5785         }
   5786     }
   5787 
   5788     // 1/(density * density) used to compute the distance between points.
   5789     // Computed in init().
   5790     private float DRAG_LAYER_INVERSE_DENSITY_SQUARED;
   5791 
   5792     // The distance between two points reported in onTouchEvent scaled by the
   5793     // density of the screen.
   5794     private static final int DRAG_LAYER_FINGER_DISTANCE = 20000;
   5795 
   5796     @Override
   5797     public boolean onHoverEvent(MotionEvent event) {
   5798         if (mNativeClass == 0) {
   5799             return false;
   5800         }
   5801         int x = viewToContentX((int) event.getX() + getScrollX());
   5802         int y = viewToContentY((int) event.getY() + getScrollY());
   5803         mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, x, y);
   5804         mWebViewPrivate.super_onHoverEvent(event);
   5805         return true;
   5806     }
   5807 
   5808     @Override
   5809     public boolean onTouchEvent(MotionEvent ev) {
   5810         if (mNativeClass == 0 || (!mWebView.isClickable() && !mWebView.isLongClickable())) {
   5811             return false;
   5812         }
   5813 
   5814         if (mInputDispatcher == null) {
   5815             return false;
   5816         }
   5817 
   5818         if (mWebView.isFocusable() && mWebView.isFocusableInTouchMode()
   5819                 && !mWebView.isFocused()) {
   5820             mWebView.requestFocus();
   5821         }
   5822 
   5823         if (mInputDispatcher.postPointerEvent(ev, getScrollX(),
   5824                 getScrollY() - getTitleHeight(), mZoomManager.getInvScale())) {
   5825             mInputDispatcher.dispatchUiEvents();
   5826             return true;
   5827         } else {
   5828             Log.w(LOGTAG, "mInputDispatcher rejected the event!");
   5829             return false;
   5830         }
   5831     }
   5832 
   5833     private float calculateDragAngle(int dx, int dy) {
   5834         dx = Math.abs(dx);
   5835         dy = Math.abs(dy);
   5836         return (float) Math.atan2(dy, dx);
   5837     }
   5838 
   5839     /*
   5840     * Common code for single touch and multi-touch.
   5841     * (x, y) denotes current focus point, which is the touch point for single touch
   5842     * and the middle point for multi-touch.
   5843     */
   5844     private void handleTouchEventCommon(MotionEvent event, int action, int x, int y) {
   5845         ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
   5846 
   5847         long eventTime = event.getEventTime();
   5848 
   5849         // Due to the touch screen edge effect, a touch closer to the edge
   5850         // always snapped to the edge. As getViewWidth() can be different from
   5851         // getWidth() due to the scrollbar, adjusting the point to match
   5852         // getViewWidth(). Same applied to the height.
   5853         x = Math.min(x, getViewWidth() - 1);
   5854         y = Math.min(y, getViewHeightWithTitle() - 1);
   5855 
   5856         int deltaX = mLastTouchX - x;
   5857         int deltaY = mLastTouchY - y;
   5858         int contentX = viewToContentX(x + getScrollX());
   5859         int contentY = viewToContentY(y + getScrollY());
   5860 
   5861         switch (action) {
   5862             case MotionEvent.ACTION_DOWN: {
   5863                 mConfirmMove = false;
   5864                 if (!mEditTextScroller.isFinished()) {
   5865                     mEditTextScroller.abortAnimation();
   5866                 }
   5867                 if (!mScroller.isFinished()) {
   5868                     // stop the current scroll animation, but if this is
   5869                     // the start of a fling, allow it to add to the current
   5870                     // fling's velocity
   5871                     mScroller.abortAnimation();
   5872                     mTouchMode = TOUCH_DRAG_START_MODE;
   5873                     mConfirmMove = true;
   5874                     nativeSetIsScrolling(false);
   5875                 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
   5876                     mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
   5877                     removeTouchHighlight();
   5878                     if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
   5879                         mTouchMode = TOUCH_DOUBLE_TAP_MODE;
   5880                     } else {
   5881                         mTouchMode = TOUCH_INIT_MODE;
   5882                     }
   5883                 } else { // the normal case
   5884                     mTouchMode = TOUCH_INIT_MODE;
   5885                     if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
   5886                         EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
   5887                                 (eventTime - mLastTouchUpTime), eventTime);
   5888                     }
   5889                     mSelectionStarted = false;
   5890                     if (mSelectingText) {
   5891                         ensureSelectionHandles();
   5892                         int shiftedY = y - getTitleHeight() + getScrollY();
   5893                         int shiftedX = x + getScrollX();
   5894                         if (mSelectHandleBaseBounds.contains(shiftedX, shiftedY)) {
   5895                             mSelectionStarted = true;
   5896                             mSelectDraggingCursor = mSelectCursorBase;
   5897                             mSelectDraggingTextQuad = mSelectCursorBaseTextQuad;
   5898                             if (mIsCaretSelection) {
   5899                                 mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
   5900                                 hidePasteButton();
   5901                             }
   5902                         } else if (mSelectHandleExtentBounds
   5903                                 .contains(shiftedX, shiftedY)) {
   5904                             mSelectionStarted = true;
   5905                             mSelectDraggingCursor = mSelectCursorExtent;
   5906                             mSelectDraggingTextQuad = mSelectCursorExtentTextQuad;
   5907                         } else if (mIsCaretSelection) {
   5908                             selectionDone();
   5909                         }
   5910                         if (DebugFlags.WEB_VIEW) {
   5911                             Log.v(LOGTAG, "select=" + contentX + "," + contentY);
   5912                         }
   5913                     }
   5914                 }
   5915                 // Trigger the link
   5916                 if (!mSelectingText && (mTouchMode == TOUCH_INIT_MODE
   5917                         || mTouchMode == TOUCH_DOUBLE_TAP_MODE)) {
   5918                     mPrivateHandler.sendEmptyMessageDelayed(
   5919                             SWITCH_TO_SHORTPRESS, TAP_TIMEOUT);
   5920                     mPrivateHandler.sendEmptyMessageDelayed(
   5921                             SWITCH_TO_LONGPRESS, LONG_PRESS_TIMEOUT);
   5922                 }
   5923                 startTouch(x, y, eventTime);
   5924                 if (mIsEditingText) {
   5925                     mTouchInEditText = mEditTextContentBounds
   5926                             .contains(contentX, contentY);
   5927                 }
   5928                 break;
   5929             }
   5930             case MotionEvent.ACTION_MOVE: {
   5931                 if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY)
   5932                         >= mTouchSlopSquare) {
   5933                     mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
   5934                     mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
   5935                     mConfirmMove = true;
   5936                     if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
   5937                         mTouchMode = TOUCH_INIT_MODE;
   5938                     }
   5939                     removeTouchHighlight();
   5940                 }
   5941                 if (mSelectingText && mSelectionStarted) {
   5942                     if (DebugFlags.WEB_VIEW) {
   5943                         Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
   5944                     }
   5945                     ViewParent parent = mWebView.getParent();
   5946                     if (parent != null) {
   5947                         parent.requestDisallowInterceptTouchEvent(true);
   5948                     }
   5949                     if (deltaX != 0 || deltaY != 0) {
   5950                         int handleX = contentX +
   5951                                 viewToContentDimension(mSelectOffset.x);
   5952                         int handleY = contentY +
   5953                                 viewToContentDimension(mSelectOffset.y);
   5954                         mSelectDraggingCursor.set(handleX, handleY);
   5955                         boolean inCursorText =
   5956                                 mSelectDraggingTextQuad.containsPoint(handleX, handleY);
   5957                         boolean inEditBounds = mEditTextContentBounds
   5958                                 .contains(handleX, handleY);
   5959                         if (mIsEditingText && !inEditBounds) {
   5960                             beginScrollEdit();
   5961                         } else {
   5962                             endScrollEdit();
   5963                         }
   5964                         boolean snapped = false;
   5965                         if (inCursorText || (mIsEditingText && !inEditBounds)) {
   5966                             snapDraggingCursor();
   5967                             snapped = true;
   5968                         }
   5969                         updateWebkitSelection(snapped);
   5970                         if (!inCursorText && mIsEditingText && inEditBounds) {
   5971                             // Visually snap even if we have moved the handle.
   5972                             snapDraggingCursor();
   5973                         }
   5974                         mLastTouchX = x;
   5975                         mLastTouchY = y;
   5976                         invalidate();
   5977                     }
   5978                     break;
   5979                 }
   5980 
   5981                 if (mTouchMode == TOUCH_DONE_MODE) {
   5982                     // no dragging during scroll zoom animation, or when prevent
   5983                     // default is yes
   5984                     break;
   5985                 }
   5986                 if (mVelocityTracker == null) {
   5987                     Log.e(LOGTAG, "Got null mVelocityTracker when "
   5988                             + " mTouchMode = " + mTouchMode);
   5989                 } else {
   5990                     mVelocityTracker.addMovement(event);
   5991                 }
   5992 
   5993                 if (mTouchMode != TOUCH_DRAG_MODE &&
   5994                         mTouchMode != TOUCH_DRAG_LAYER_MODE &&
   5995                         mTouchMode != TOUCH_DRAG_TEXT_MODE) {
   5996 
   5997                     if (!mConfirmMove) {
   5998                         break;
   5999                     }
   6000 
   6001                     // Only lock dragging to one axis if we don't have a scale in progress.
   6002                     // Scaling implies free-roaming movement. Note this is only ever a question
   6003                     // if mZoomManager.supportsPanDuringZoom() is true.
   6004                     mAverageAngle = calculateDragAngle(deltaX, deltaY);
   6005                     if (detector == null || !detector.isInProgress()) {
   6006                         // if it starts nearly horizontal or vertical, enforce it
   6007                         if (mAverageAngle < HSLOPE_TO_START_SNAP) {
   6008                             mSnapScrollMode = SNAP_X;
   6009                             mSnapPositive = deltaX > 0;
   6010                             mAverageAngle = ANGLE_HORIZ;
   6011                         } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
   6012                             mSnapScrollMode = SNAP_Y;
   6013                             mSnapPositive = deltaY > 0;
   6014                             mAverageAngle = ANGLE_VERT;
   6015                         }
   6016                     }
   6017 
   6018                     mTouchMode = TOUCH_DRAG_MODE;
   6019                     mLastTouchX = x;
   6020                     mLastTouchY = y;
   6021                     deltaX = 0;
   6022                     deltaY = 0;
   6023 
   6024                     startScrollingLayer(x, y);
   6025                     startDrag();
   6026                 }
   6027 
   6028                 // do pan
   6029                 boolean keepScrollBarsVisible = false;
   6030                 if (deltaX == 0 && deltaY == 0) {
   6031                     keepScrollBarsVisible = true;
   6032                 } else {
   6033                     mAverageAngle +=
   6034                         (calculateDragAngle(deltaX, deltaY) - mAverageAngle)
   6035                         / MMA_WEIGHT_N;
   6036                     if (mSnapScrollMode != SNAP_NONE) {
   6037                         if (mSnapScrollMode == SNAP_Y) {
   6038                             // radical change means getting out of snap mode
   6039                             if (mAverageAngle < VSLOPE_TO_BREAK_SNAP) {
   6040                                 mSnapScrollMode = SNAP_NONE;
   6041                             }
   6042                         }
   6043                         if (mSnapScrollMode == SNAP_X) {
   6044                             // radical change means getting out of snap mode
   6045                             if (mAverageAngle > HSLOPE_TO_BREAK_SNAP) {
   6046                                 mSnapScrollMode = SNAP_NONE;
   6047                             }
   6048                         }
   6049                     } else {
   6050                         if (mAverageAngle < HSLOPE_TO_START_SNAP) {
   6051                             mSnapScrollMode = SNAP_X;
   6052                             mSnapPositive = deltaX > 0;
   6053                             mAverageAngle = (mAverageAngle + ANGLE_HORIZ) / 2;
   6054                         } else if (mAverageAngle > VSLOPE_TO_START_SNAP) {
   6055                             mSnapScrollMode = SNAP_Y;
   6056                             mSnapPositive = deltaY > 0;
   6057                             mAverageAngle = (mAverageAngle + ANGLE_VERT) / 2;
   6058                         }
   6059                     }
   6060                     if (mSnapScrollMode != SNAP_NONE) {
   6061                         if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
   6062                             deltaY = 0;
   6063                         } else {
   6064                             deltaX = 0;
   6065                         }
   6066                     }
   6067                     if (deltaX * deltaX + deltaY * deltaY > mTouchSlopSquare) {
   6068                         mHeldMotionless = MOTIONLESS_FALSE;
   6069                     } else {
   6070                         mHeldMotionless = MOTIONLESS_TRUE;
   6071                         keepScrollBarsVisible = true;
   6072                     }
   6073 
   6074                     mLastTouchTime = eventTime;
   6075                     boolean allDrag = doDrag(deltaX, deltaY);
   6076                     if (allDrag) {
   6077                         mLastTouchX = x;
   6078                         mLastTouchY = y;
   6079                     } else {
   6080                         int contentDeltaX = (int)Math.floor(deltaX * mZoomManager.getInvScale());
   6081                         int roundedDeltaX = contentToViewDimension(contentDeltaX);
   6082                         int contentDeltaY = (int)Math.floor(deltaY * mZoomManager.getInvScale());
   6083                         int roundedDeltaY = contentToViewDimension(contentDeltaY);
   6084                         mLastTouchX -= roundedDeltaX;
   6085                         mLastTouchY -= roundedDeltaY;
   6086                     }
   6087                 }
   6088 
   6089                 break;
   6090             }
   6091             case MotionEvent.ACTION_UP: {
   6092                 if (mIsEditingText && mSelectionStarted) {
   6093                     endScrollEdit();
   6094                     mPrivateHandler.sendEmptyMessageDelayed(SCROLL_HANDLE_INTO_VIEW,
   6095                             TEXT_SCROLL_FIRST_SCROLL_MS);
   6096                     if (!mConfirmMove && mIsCaretSelection) {
   6097                         showPasteWindow();
   6098                         stopTouch();
   6099                         break;
   6100                     }
   6101                 }
   6102                 mLastTouchUpTime = eventTime;
   6103                 if (mSentAutoScrollMessage) {
   6104                     mAutoScrollX = mAutoScrollY = 0;
   6105                 }
   6106                 switch (mTouchMode) {
   6107                     case TOUCH_DOUBLE_TAP_MODE: // double tap
   6108                         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
   6109                         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
   6110                         mTouchMode = TOUCH_DONE_MODE;
   6111                         break;
   6112                     case TOUCH_INIT_MODE: // tap
   6113                     case TOUCH_SHORTPRESS_START_MODE:
   6114                     case TOUCH_SHORTPRESS_MODE:
   6115                         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
   6116                         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
   6117                         if (!mConfirmMove) {
   6118                             if (mSelectingText) {
   6119                                 // tapping on selection or controls does nothing
   6120                                 if (!mSelectionStarted) {
   6121                                     selectionDone();
   6122                                 }
   6123                                 break;
   6124                             }
   6125                             // only trigger double tap if the WebView is
   6126                             // scalable
   6127                             if (mTouchMode == TOUCH_INIT_MODE
   6128                                     && (canZoomIn() || canZoomOut())) {
   6129                                 mPrivateHandler.sendEmptyMessageDelayed(
   6130                                         RELEASE_SINGLE_TAP, ViewConfiguration
   6131                                                 .getDoubleTapTimeout());
   6132                             }
   6133                             break;
   6134                         }
   6135                     case TOUCH_DRAG_MODE:
   6136                     case TOUCH_DRAG_LAYER_MODE:
   6137                     case TOUCH_DRAG_TEXT_MODE:
   6138                         mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
   6139                         // if the user waits a while w/o moving before the
   6140                         // up, we don't want to do a fling
   6141                         if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
   6142                             if (mVelocityTracker == null) {
   6143                                 Log.e(LOGTAG, "Got null mVelocityTracker");
   6144                             } else {
   6145                                 mVelocityTracker.addMovement(event);
   6146                             }
   6147                             // set to MOTIONLESS_IGNORE so that it won't keep
   6148                             // removing and sending message in
   6149                             // drawCoreAndCursorRing()
   6150                             mHeldMotionless = MOTIONLESS_IGNORE;
   6151                             doFling();
   6152                             break;
   6153                         } else {
   6154                             if (mScroller.springBack(getScrollX(), getScrollY(), 0,
   6155                                     computeMaxScrollX(), 0,
   6156                                     computeMaxScrollY())) {
   6157                                 invalidate();
   6158                             }
   6159                         }
   6160                         // redraw in high-quality, as we're done dragging
   6161                         mHeldMotionless = MOTIONLESS_TRUE;
   6162                         invalidate();
   6163                         // fall through
   6164                     case TOUCH_DRAG_START_MODE:
   6165                         // TOUCH_DRAG_START_MODE should not happen for the real
   6166                         // device as we almost certain will get a MOVE. But this
   6167                         // is possible on emulator.
   6168                         mLastVelocity = 0;
   6169                         WebViewCore.resumePriority();
   6170                         if (!mSelectingText) {
   6171                             WebViewCore.resumeUpdatePicture(mWebViewCore);
   6172                         }
   6173                         break;
   6174                 }
   6175                 stopTouch();
   6176                 break;
   6177             }
   6178             case MotionEvent.ACTION_CANCEL: {
   6179                 if (mTouchMode == TOUCH_DRAG_MODE) {
   6180                     mScroller.springBack(getScrollX(), getScrollY(), 0,
   6181                             computeMaxScrollX(), 0, computeMaxScrollY());
   6182                     invalidate();
   6183                 }
   6184                 cancelTouch();
   6185                 break;
   6186             }
   6187         }
   6188     }
   6189 
   6190     /**
   6191      * Returns the text scroll speed in content pixels per millisecond based on
   6192      * the touch location.
   6193      * @param coordinate The x or y touch coordinate in content space
   6194      * @param min The minimum coordinate (x or y) of the edit content bounds
   6195      * @param max The maximum coordinate (x or y) of the edit content bounds
   6196      */
   6197     private static float getTextScrollSpeed(int coordinate, int min, int max) {
   6198         if (coordinate < min) {
   6199             return (coordinate - min) * TEXT_SCROLL_RATE;
   6200         } else if (coordinate >= max) {
   6201             return (coordinate - max + 1) * TEXT_SCROLL_RATE;
   6202         } else {
   6203             return 0.0f;
   6204         }
   6205     }
   6206 
   6207     private static int getSelectionCoordinate(int coordinate, int min, int max) {
   6208         return Math.max(Math.min(coordinate, max), min);
   6209     }
   6210 
   6211     private void beginScrollEdit() {
   6212         if (mLastEditScroll == 0) {
   6213             mLastEditScroll = SystemClock.uptimeMillis() -
   6214                     TEXT_SCROLL_FIRST_SCROLL_MS;
   6215             scrollEditWithCursor();
   6216         }
   6217     }
   6218 
   6219     private void scrollDraggedSelectionHandleIntoView() {
   6220         if (mSelectDraggingCursor == null) {
   6221             return;
   6222         }
   6223         int x = mSelectDraggingCursor.x;
   6224         int y = mSelectDraggingCursor.y;
   6225         if (!mEditTextContentBounds.contains(x,y)) {
   6226             int left = Math.min(0, x - mEditTextContentBounds.left - EDIT_RECT_BUFFER);
   6227             int right = Math.max(0, x - mEditTextContentBounds.right + EDIT_RECT_BUFFER);
   6228             int deltaX = left + right;
   6229             int above = Math.min(0, y - mEditTextContentBounds.top - EDIT_RECT_BUFFER);
   6230             int below = Math.max(0, y - mEditTextContentBounds.bottom + EDIT_RECT_BUFFER);
   6231             int deltaY = above + below;
   6232             if (deltaX != 0 || deltaY != 0) {
   6233                 int scrollX = getTextScrollX() + deltaX;
   6234                 int scrollY = getTextScrollY() + deltaY;
   6235                 scrollX = clampBetween(scrollX, 0, getMaxTextScrollX());
   6236                 scrollY = clampBetween(scrollY, 0, getMaxTextScrollY());
   6237                 scrollEditText(scrollX, scrollY);
   6238             }
   6239         }
   6240     }
   6241 
   6242     private void endScrollEdit() {
   6243         mLastEditScroll = 0;
   6244     }
   6245 
   6246     private static int clampBetween(int value, int min, int max) {
   6247         return Math.max(min, Math.min(value, max));
   6248     }
   6249 
   6250     private static int getTextScrollDelta(float speed, long deltaT) {
   6251         float distance = speed * deltaT;
   6252         int intDistance = (int)Math.floor(distance);
   6253         float probability = distance - intDistance;
   6254         if (Math.random() < probability) {
   6255             intDistance++;
   6256         }
   6257         return intDistance;
   6258     }
   6259     /**
   6260      * Scrolls edit text a distance based on the last touch point,
   6261      * the last scroll time, and the edit text content bounds.
   6262      */
   6263     private void scrollEditWithCursor() {
   6264         if (mLastEditScroll != 0) {
   6265             int x = viewToContentX(mLastTouchX + getScrollX() + mSelectOffset.x);
   6266             float scrollSpeedX = getTextScrollSpeed(x, mEditTextContentBounds.left,
   6267                     mEditTextContentBounds.right);
   6268             int y = viewToContentY(mLastTouchY + getScrollY() + mSelectOffset.y);
   6269             float scrollSpeedY = getTextScrollSpeed(y, mEditTextContentBounds.top,
   6270                     mEditTextContentBounds.bottom);
   6271             if (scrollSpeedX == 0.0f && scrollSpeedY == 0.0f) {
   6272                 endScrollEdit();
   6273             } else {
   6274                 long currentTime = SystemClock.uptimeMillis();
   6275                 long timeSinceLastUpdate = currentTime - mLastEditScroll;
   6276                 int deltaX = getTextScrollDelta(scrollSpeedX, timeSinceLastUpdate);
   6277                 int deltaY = getTextScrollDelta(scrollSpeedY, timeSinceLastUpdate);
   6278                 int scrollX = getTextScrollX() + deltaX;
   6279                 scrollX = clampBetween(scrollX, 0, getMaxTextScrollX());
   6280                 int scrollY = getTextScrollY() + deltaY;
   6281                 scrollY = clampBetween(scrollY, 0, getMaxTextScrollY());
   6282 
   6283                 mLastEditScroll = currentTime;
   6284                 if (scrollX == getTextScrollX() && scrollY == getTextScrollY()) {
   6285                     // By probability no text scroll this time. Try again later.
   6286                     mPrivateHandler.sendEmptyMessageDelayed(SCROLL_EDIT_TEXT,
   6287                             TEXT_SCROLL_FIRST_SCROLL_MS);
   6288                 } else {
   6289                     int selectionX = getSelectionCoordinate(x,
   6290                             mEditTextContentBounds.left, mEditTextContentBounds.right);
   6291                     int selectionY = getSelectionCoordinate(y,
   6292                             mEditTextContentBounds.top, mEditTextContentBounds.bottom);
   6293                     int oldX = mSelectDraggingCursor.x;
   6294                     int oldY = mSelectDraggingCursor.y;
   6295                     mSelectDraggingCursor.set(selectionX, selectionY);
   6296                     updateWebkitSelection(false);
   6297                     scrollEditText(scrollX, scrollY);
   6298                     mSelectDraggingCursor.set(oldX, oldY);
   6299                 }
   6300             }
   6301         }
   6302     }
   6303 
   6304     private void startTouch(float x, float y, long eventTime) {
   6305         // Remember where the motion event started
   6306         mStartTouchX = mLastTouchX = Math.round(x);
   6307         mStartTouchY = mLastTouchY = Math.round(y);
   6308         mLastTouchTime = eventTime;
   6309         mVelocityTracker = VelocityTracker.obtain();
   6310         mSnapScrollMode = SNAP_NONE;
   6311     }
   6312 
   6313     private void startDrag() {
   6314         WebViewCore.reducePriority();
   6315         // to get better performance, pause updating the picture
   6316         WebViewCore.pauseUpdatePicture(mWebViewCore);
   6317         nativeSetIsScrolling(true);
   6318 
   6319         if (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF
   6320                 || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF) {
   6321             mZoomManager.invokeZoomPicker();
   6322         }
   6323     }
   6324 
   6325     private boolean doDrag(int deltaX, int deltaY) {
   6326         boolean allDrag = true;
   6327         if ((deltaX | deltaY) != 0) {
   6328             int oldX = getScrollX();
   6329             int oldY = getScrollY();
   6330             int rangeX = computeMaxScrollX();
   6331             int rangeY = computeMaxScrollY();
   6332             final int contentX = (int)Math.floor(deltaX * mZoomManager.getInvScale());
   6333             final int contentY = (int)Math.floor(deltaY * mZoomManager.getInvScale());
   6334 
   6335             // Assume page scrolling and change below if we're wrong
   6336             mTouchMode = TOUCH_DRAG_MODE;
   6337 
   6338             // Check special scrolling before going to main page scrolling.
   6339             if (mIsEditingText && mTouchInEditText && canTextScroll(deltaX, deltaY)) {
   6340                 // Edit text scrolling
   6341                 oldX = getTextScrollX();
   6342                 rangeX = getMaxTextScrollX();
   6343                 deltaX = contentX;
   6344                 oldY = getTextScrollY();
   6345                 rangeY = getMaxTextScrollY();
   6346                 deltaY = contentY;
   6347                 mTouchMode = TOUCH_DRAG_TEXT_MODE;
   6348                 allDrag = false;
   6349             } else if (mCurrentScrollingLayerId != 0) {
   6350                 // Check the scrolling bounds to see if we will actually do any
   6351                 // scrolling.  The rectangle is in document coordinates.
   6352                 final int maxX = mScrollingLayerRect.right;
   6353                 final int maxY = mScrollingLayerRect.bottom;
   6354                 final int resultX = clampBetween(maxX, 0,
   6355                         mScrollingLayerRect.left + contentX);
   6356                 final int resultY = clampBetween(maxY, 0,
   6357                         mScrollingLayerRect.top + contentY);
   6358 
   6359                 if (resultX != mScrollingLayerRect.left
   6360                         || resultY != mScrollingLayerRect.top
   6361                         || (contentX | contentY) == 0) {
   6362                     // In case we switched to dragging the page.
   6363                     mTouchMode = TOUCH_DRAG_LAYER_MODE;
   6364                     deltaX = contentX;
   6365                     deltaY = contentY;
   6366                     oldX = mScrollingLayerRect.left;
   6367                     oldY = mScrollingLayerRect.top;
   6368                     rangeX = maxX;
   6369                     rangeY = maxY;
   6370                     allDrag = false;
   6371                 }
   6372             }
   6373 
   6374             if (mOverScrollGlow != null) {
   6375                 mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
   6376             }
   6377 
   6378             mWebViewPrivate.overScrollBy(deltaX, deltaY, oldX, oldY,
   6379                     rangeX, rangeY,
   6380                     mOverscrollDistance, mOverscrollDistance, true);
   6381             if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
   6382                 invalidate();
   6383             }
   6384         }
   6385         mZoomManager.keepZoomPickerVisible();
   6386         return allDrag;
   6387     }
   6388 
   6389     private void stopTouch() {
   6390         if (mScroller.isFinished() && !mSelectingText
   6391                 && (mTouchMode == TOUCH_DRAG_MODE
   6392                 || mTouchMode == TOUCH_DRAG_LAYER_MODE)) {
   6393             WebViewCore.resumePriority();
   6394             WebViewCore.resumeUpdatePicture(mWebViewCore);
   6395             nativeSetIsScrolling(false);
   6396         }
   6397 
   6398         // we also use mVelocityTracker == null to tell us that we are
   6399         // not "moving around", so we can take the slower/prettier
   6400         // mode in the drawing code
   6401         if (mVelocityTracker != null) {
   6402             mVelocityTracker.recycle();
   6403             mVelocityTracker = null;
   6404         }
   6405 
   6406         // Release any pulled glows
   6407         if (mOverScrollGlow != null) {
   6408             mOverScrollGlow.releaseAll();
   6409         }
   6410 
   6411         if (mSelectingText) {
   6412             mSelectionStarted = false;
   6413             syncSelectionCursors();
   6414             if (mIsCaretSelection) {
   6415                 resetCaretTimer();
   6416             }
   6417             invalidate();
   6418         }
   6419     }
   6420 
   6421     private void cancelTouch() {
   6422         // we also use mVelocityTracker == null to tell us that we are
   6423         // not "moving around", so we can take the slower/prettier
   6424         // mode in the drawing code
   6425         if (mVelocityTracker != null) {
   6426             mVelocityTracker.recycle();
   6427             mVelocityTracker = null;
   6428         }
   6429 
   6430         if ((mTouchMode == TOUCH_DRAG_MODE
   6431                 || mTouchMode == TOUCH_DRAG_LAYER_MODE) && !mSelectingText) {
   6432             WebViewCore.resumePriority();
   6433             WebViewCore.resumeUpdatePicture(mWebViewCore);
   6434             nativeSetIsScrolling(false);
   6435         }
   6436         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
   6437         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
   6438         mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
   6439         removeTouchHighlight();
   6440         mHeldMotionless = MOTIONLESS_TRUE;
   6441         mTouchMode = TOUCH_DONE_MODE;
   6442     }
   6443 
   6444     private void snapDraggingCursor() {
   6445         float scale = scaleAlongSegment(
   6446                 mSelectDraggingCursor.x, mSelectDraggingCursor.y,
   6447                 mSelectDraggingTextQuad.p4, mSelectDraggingTextQuad.p3);
   6448         // clamp scale to ensure point is on the bottom segment
   6449         scale = Math.max(0.0f, scale);
   6450         scale = Math.min(scale, 1.0f);
   6451         float newX = scaleCoordinate(scale,
   6452                 mSelectDraggingTextQuad.p4.x, mSelectDraggingTextQuad.p3.x);
   6453         float newY = scaleCoordinate(scale,
   6454                 mSelectDraggingTextQuad.p4.y, mSelectDraggingTextQuad.p3.y);
   6455         int x = Math.round(newX);
   6456         int y = Math.round(newY);
   6457         if (mIsEditingText) {
   6458             x = clampBetween(x, mEditTextContentBounds.left,
   6459                     mEditTextContentBounds.right);
   6460             y = clampBetween(y, mEditTextContentBounds.top,
   6461                     mEditTextContentBounds.bottom);
   6462         }
   6463         mSelectDraggingCursor.set(x, y);
   6464     }
   6465 
   6466     private static float scaleCoordinate(float scale, float coord1, float coord2) {
   6467         float diff = coord2 - coord1;
   6468         return coord1 + (scale * diff);
   6469     }
   6470 
   6471     @Override
   6472     public boolean onGenericMotionEvent(MotionEvent event) {
   6473         if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
   6474             switch (event.getAction()) {
   6475                 case MotionEvent.ACTION_SCROLL: {
   6476                     final float vscroll;
   6477                     final float hscroll;
   6478                     if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
   6479                         vscroll = 0;
   6480                         hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
   6481                     } else {
   6482                         vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
   6483                         hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
   6484                     }
   6485                     if (hscroll != 0 || vscroll != 0) {
   6486                         final int vdelta = (int) (vscroll *
   6487                                 mWebViewPrivate.getVerticalScrollFactor());
   6488                         final int hdelta = (int) (hscroll *
   6489                                 mWebViewPrivate.getHorizontalScrollFactor());
   6490 
   6491                         abortAnimation();
   6492                         int oldTouchMode = mTouchMode;
   6493                         startScrollingLayer(event.getX(), event.getY());
   6494                         doDrag(hdelta, vdelta);
   6495                         mTouchMode = oldTouchMode;
   6496                         return true;
   6497                     }
   6498                 }
   6499             }
   6500         }
   6501         return mWebViewPrivate.super_onGenericMotionEvent(event);
   6502     }
   6503 
   6504     private long mTrackballFirstTime = 0;
   6505     private long mTrackballLastTime = 0;
   6506     private float mTrackballRemainsX = 0.0f;
   6507     private float mTrackballRemainsY = 0.0f;
   6508     private int mTrackballXMove = 0;
   6509     private int mTrackballYMove = 0;
   6510     private boolean mSelectingText = false;
   6511     private boolean mShowTextSelectionExtra = false;
   6512     private boolean mSelectionStarted = false;
   6513     private static final int TRACKBALL_KEY_TIMEOUT = 1000;
   6514     private static final int TRACKBALL_TIMEOUT = 200;
   6515     private static final int TRACKBALL_WAIT = 100;
   6516     private static final int TRACKBALL_SCALE = 400;
   6517     private static final int TRACKBALL_SCROLL_COUNT = 5;
   6518     private static final int TRACKBALL_MOVE_COUNT = 10;
   6519     private static final int TRACKBALL_MULTIPLIER = 3;
   6520     private static final int SELECT_CURSOR_OFFSET = 16;
   6521     private static final int SELECT_SCROLL = 5;
   6522     private int mSelectX = 0;
   6523     private int mSelectY = 0;
   6524     private boolean mTrackballDown = false;
   6525     private long mTrackballUpTime = 0;
   6526     private long mLastCursorTime = 0;
   6527     private Rect mLastCursorBounds;
   6528     private SelectionHandleAlpha mBaseAlpha = new SelectionHandleAlpha();
   6529     private SelectionHandleAlpha mExtentAlpha = new SelectionHandleAlpha();
   6530     private ObjectAnimator mBaseHandleAlphaAnimator =
   6531             ObjectAnimator.ofInt(mBaseAlpha, "alpha", 0);
   6532     private ObjectAnimator mExtentHandleAlphaAnimator =
   6533             ObjectAnimator.ofInt(mExtentAlpha, "alpha", 0);
   6534 
   6535     // Set by default; BrowserActivity clears to interpret trackball data
   6536     // directly for movement. Currently, the framework only passes
   6537     // arrow key events, not trackball events, from one child to the next
   6538     private boolean mMapTrackballToArrowKeys = true;
   6539 
   6540     private DrawData mDelaySetPicture;
   6541     private DrawData mLoadedPicture;
   6542 
   6543     @Override
   6544     public void setMapTrackballToArrowKeys(boolean setMap) {
   6545         mMapTrackballToArrowKeys = setMap;
   6546     }
   6547 
   6548     void resetTrackballTime() {
   6549         mTrackballLastTime = 0;
   6550     }
   6551 
   6552     @Override
   6553     public boolean onTrackballEvent(MotionEvent ev) {
   6554         long time = ev.getEventTime();
   6555         if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
   6556             if (ev.getY() > 0) pageDown(true);
   6557             if (ev.getY() < 0) pageUp(true);
   6558             return true;
   6559         }
   6560         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
   6561             if (mSelectingText) {
   6562                 return true; // discard press if copy in progress
   6563             }
   6564             mTrackballDown = true;
   6565             if (mNativeClass == 0) {
   6566                 return false;
   6567             }
   6568             if (DebugFlags.WEB_VIEW) {
   6569                 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
   6570                         + " time=" + time
   6571                         + " mLastCursorTime=" + mLastCursorTime);
   6572             }
   6573             if (mWebView.isInTouchMode()) mWebView.requestFocusFromTouch();
   6574             return false; // let common code in onKeyDown at it
   6575         }
   6576         if (ev.getAction() == MotionEvent.ACTION_UP) {
   6577             // LONG_PRESS_CENTER is set in common onKeyDown
   6578             mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
   6579             mTrackballDown = false;
   6580             mTrackballUpTime = time;
   6581             if (mSelectingText) {
   6582                 copySelection();
   6583                 selectionDone();
   6584                 return true; // discard press if copy in progress
   6585             }
   6586             if (DebugFlags.WEB_VIEW) {
   6587                 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
   6588                         + " time=" + time
   6589                 );
   6590             }
   6591             return false; // let common code in onKeyUp at it
   6592         }
   6593         if ((mMapTrackballToArrowKeys && (ev.getMetaState() & KeyEvent.META_SHIFT_ON) == 0) ||
   6594                 AccessibilityManager.getInstance(mContext).isEnabled()) {
   6595             if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
   6596             return false;
   6597         }
   6598         if (mTrackballDown) {
   6599             if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
   6600             return true; // discard move if trackball is down
   6601         }
   6602         if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
   6603             if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
   6604             return true;
   6605         }
   6606         // TODO: alternatively we can do panning as touch does
   6607         switchOutDrawHistory();
   6608         if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
   6609             if (DebugFlags.WEB_VIEW) {
   6610                 Log.v(LOGTAG, "onTrackballEvent time="
   6611                         + time + " last=" + mTrackballLastTime);
   6612             }
   6613             mTrackballFirstTime = time;
   6614             mTrackballXMove = mTrackballYMove = 0;
   6615         }
   6616         mTrackballLastTime = time;
   6617         if (DebugFlags.WEB_VIEW) {
   6618             Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
   6619         }
   6620         mTrackballRemainsX += ev.getX();
   6621         mTrackballRemainsY += ev.getY();
   6622         doTrackball(time, ev.getMetaState());
   6623         return true;
   6624     }
   6625 
   6626     private int scaleTrackballX(float xRate, int width) {
   6627         int xMove = (int) (xRate / TRACKBALL_SCALE * width);
   6628         int nextXMove = xMove;
   6629         if (xMove > 0) {
   6630             if (xMove > mTrackballXMove) {
   6631                 xMove -= mTrackballXMove;
   6632             }
   6633         } else if (xMove < mTrackballXMove) {
   6634             xMove -= mTrackballXMove;
   6635         }
   6636         mTrackballXMove = nextXMove;
   6637         return xMove;
   6638     }
   6639 
   6640     private int scaleTrackballY(float yRate, int height) {
   6641         int yMove = (int) (yRate / TRACKBALL_SCALE * height);
   6642         int nextYMove = yMove;
   6643         if (yMove > 0) {
   6644             if (yMove > mTrackballYMove) {
   6645                 yMove -= mTrackballYMove;
   6646             }
   6647         } else if (yMove < mTrackballYMove) {
   6648             yMove -= mTrackballYMove;
   6649         }
   6650         mTrackballYMove = nextYMove;
   6651         return yMove;
   6652     }
   6653 
   6654     private int keyCodeToSoundsEffect(int keyCode) {
   6655         switch(keyCode) {
   6656             case KeyEvent.KEYCODE_DPAD_UP:
   6657                 return SoundEffectConstants.NAVIGATION_UP;
   6658             case KeyEvent.KEYCODE_DPAD_RIGHT:
   6659                 return SoundEffectConstants.NAVIGATION_RIGHT;
   6660             case KeyEvent.KEYCODE_DPAD_DOWN:
   6661                 return SoundEffectConstants.NAVIGATION_DOWN;
   6662             case KeyEvent.KEYCODE_DPAD_LEFT:
   6663                 return SoundEffectConstants.NAVIGATION_LEFT;
   6664         }
   6665         return 0;
   6666     }
   6667 
   6668     private void doTrackball(long time, int metaState) {
   6669         int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
   6670         if (elapsed == 0) {
   6671             elapsed = TRACKBALL_TIMEOUT;
   6672         }
   6673         float xRate = mTrackballRemainsX * 1000 / elapsed;
   6674         float yRate = mTrackballRemainsY * 1000 / elapsed;
   6675         int viewWidth = getViewWidth();
   6676         int viewHeight = getViewHeight();
   6677         float ax = Math.abs(xRate);
   6678         float ay = Math.abs(yRate);
   6679         float maxA = Math.max(ax, ay);
   6680         if (DebugFlags.WEB_VIEW) {
   6681             Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
   6682                     + " xRate=" + xRate
   6683                     + " yRate=" + yRate
   6684                     + " mTrackballRemainsX=" + mTrackballRemainsX
   6685                     + " mTrackballRemainsY=" + mTrackballRemainsY);
   6686         }
   6687         int width = mContentWidth - viewWidth;
   6688         int height = mContentHeight - viewHeight;
   6689         if (width < 0) width = 0;
   6690         if (height < 0) height = 0;
   6691         ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
   6692         ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
   6693         maxA = Math.max(ax, ay);
   6694         int count = Math.max(0, (int) maxA);
   6695         int oldScrollX = getScrollX();
   6696         int oldScrollY = getScrollY();
   6697         if (count > 0) {
   6698             int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
   6699                     KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
   6700                     mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
   6701                     KeyEvent.KEYCODE_DPAD_RIGHT;
   6702             count = Math.min(count, TRACKBALL_MOVE_COUNT);
   6703             if (DebugFlags.WEB_VIEW) {
   6704                 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
   6705                         + " count=" + count
   6706                         + " mTrackballRemainsX=" + mTrackballRemainsX
   6707                         + " mTrackballRemainsY=" + mTrackballRemainsY);
   6708             }
   6709             if (mNativeClass != 0) {
   6710                 for (int i = 0; i < count; i++) {
   6711                     letPageHandleNavKey(selectKeyCode, time, true, metaState);
   6712                 }
   6713                 letPageHandleNavKey(selectKeyCode, time, false, metaState);
   6714             }
   6715             mTrackballRemainsX = mTrackballRemainsY = 0;
   6716         }
   6717         if (count >= TRACKBALL_SCROLL_COUNT) {
   6718             int xMove = scaleTrackballX(xRate, width);
   6719             int yMove = scaleTrackballY(yRate, height);
   6720             if (DebugFlags.WEB_VIEW) {
   6721                 Log.v(LOGTAG, "doTrackball pinScrollBy"
   6722                         + " count=" + count
   6723                         + " xMove=" + xMove + " yMove=" + yMove
   6724                         + " mScrollX-oldScrollX=" + (getScrollX()-oldScrollX)
   6725                         + " mScrollY-oldScrollY=" + (getScrollY()-oldScrollY)
   6726                         );
   6727             }
   6728             if (Math.abs(getScrollX() - oldScrollX) > Math.abs(xMove)) {
   6729                 xMove = 0;
   6730             }
   6731             if (Math.abs(getScrollY() - oldScrollY) > Math.abs(yMove)) {
   6732                 yMove = 0;
   6733             }
   6734             if (xMove != 0 || yMove != 0) {
   6735                 pinScrollBy(xMove, yMove, true, 0);
   6736             }
   6737         }
   6738     }
   6739 
   6740     /**
   6741      * Compute the maximum horizontal scroll position. Used by {@link OverScrollGlow}.
   6742      * @return Maximum horizontal scroll position within real content
   6743      */
   6744     int computeMaxScrollX() {
   6745         return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
   6746     }
   6747 
   6748     /**
   6749      * Compute the maximum vertical scroll position. Used by {@link OverScrollGlow}.
   6750      * @return Maximum vertical scroll position within real content
   6751      */
   6752     int computeMaxScrollY() {
   6753         return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
   6754                 - getViewHeightWithTitle(), 0);
   6755     }
   6756 
   6757     boolean updateScrollCoordinates(int x, int y) {
   6758         int oldX = getScrollX();
   6759         int oldY = getScrollY();
   6760         setScrollXRaw(x);
   6761         setScrollYRaw(y);
   6762         if (oldX != getScrollX() || oldY != getScrollY()) {
   6763             mWebViewPrivate.onScrollChanged(getScrollX(), getScrollY(), oldX, oldY);
   6764             return true;
   6765         } else {
   6766             return false;
   6767         }
   6768     }
   6769 
   6770     @Override
   6771     public void flingScroll(int vx, int vy) {
   6772         mScroller.fling(getScrollX(), getScrollY(), vx, vy, 0, computeMaxScrollX(), 0,
   6773                 computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
   6774         invalidate();
   6775     }
   6776 
   6777     private void doFling() {
   6778         if (mVelocityTracker == null) {
   6779             return;
   6780         }
   6781         int maxX = computeMaxScrollX();
   6782         int maxY = computeMaxScrollY();
   6783 
   6784         mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
   6785         int vx = (int) mVelocityTracker.getXVelocity();
   6786         int vy = (int) mVelocityTracker.getYVelocity();
   6787 
   6788         int scrollX = getScrollX();
   6789         int scrollY = getScrollY();
   6790         int overscrollDistance = mOverscrollDistance;
   6791         int overflingDistance = mOverflingDistance;
   6792 
   6793         // Use the layer's scroll data if applicable.
   6794         if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
   6795             scrollX = mScrollingLayerRect.left;
   6796             scrollY = mScrollingLayerRect.top;
   6797             maxX = mScrollingLayerRect.right;
   6798             maxY = mScrollingLayerRect.bottom;
   6799             // No overscrolling for layers.
   6800             overscrollDistance = overflingDistance = 0;
   6801         } else if (mTouchMode == TOUCH_DRAG_TEXT_MODE) {
   6802             scrollX = getTextScrollX();
   6803             scrollY = getTextScrollY();
   6804             maxX = getMaxTextScrollX();
   6805             maxY = getMaxTextScrollY();
   6806             // No overscrolling for edit text.
   6807             overscrollDistance = overflingDistance = 0;
   6808         }
   6809 
   6810         if (mSnapScrollMode != SNAP_NONE) {
   6811             if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
   6812                 vy = 0;
   6813             } else {
   6814                 vx = 0;
   6815             }
   6816         }
   6817         if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
   6818             WebViewCore.resumePriority();
   6819             if (!mSelectingText) {
   6820                 WebViewCore.resumeUpdatePicture(mWebViewCore);
   6821             }
   6822             if (mScroller.springBack(scrollX, scrollY, 0, maxX, 0, maxY)) {
   6823                 invalidate();
   6824             }
   6825             return;
   6826         }
   6827         float currentVelocity = mScroller.getCurrVelocity();
   6828         float velocity = (float) Math.hypot(vx, vy);
   6829         if (mLastVelocity > 0 && currentVelocity > 0 && velocity
   6830                 > mLastVelocity * MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION) {
   6831             float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
   6832                     - Math.atan2(vy, vx)));
   6833             final float circle = (float) (Math.PI) * 2.0f;
   6834             if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
   6835                 vx += currentVelocity * mLastVelX / mLastVelocity;
   6836                 vy += currentVelocity * mLastVelY / mLastVelocity;
   6837                 velocity = (float) Math.hypot(vx, vy);
   6838                 if (DebugFlags.WEB_VIEW) {
   6839                     Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
   6840                 }
   6841             } else if (DebugFlags.WEB_VIEW) {
   6842                 Log.v(LOGTAG, "doFling missed " + deltaR / circle);
   6843             }
   6844         } else if (DebugFlags.WEB_VIEW) {
   6845             Log.v(LOGTAG, "doFling start last=" + mLastVelocity
   6846                     + " current=" + currentVelocity
   6847                     + " vx=" + vx + " vy=" + vy
   6848                     + " maxX=" + maxX + " maxY=" + maxY
   6849                     + " scrollX=" + scrollX + " scrollY=" + scrollY
   6850                     + " layer=" + mCurrentScrollingLayerId);
   6851         }
   6852 
   6853         // Allow sloppy flings without overscrolling at the edges.
   6854         if ((scrollX == 0 || scrollX == maxX) && Math.abs(vx) < Math.abs(vy)) {
   6855             vx = 0;
   6856         }
   6857         if ((scrollY == 0 || scrollY == maxY) && Math.abs(vy) < Math.abs(vx)) {
   6858             vy = 0;
   6859         }
   6860 
   6861         if (overscrollDistance < overflingDistance) {
   6862             if ((vx > 0 && scrollX == -overscrollDistance) ||
   6863                     (vx < 0 && scrollX == maxX + overscrollDistance)) {
   6864                 vx = 0;
   6865             }
   6866             if ((vy > 0 && scrollY == -overscrollDistance) ||
   6867                     (vy < 0 && scrollY == maxY + overscrollDistance)) {
   6868                 vy = 0;
   6869             }
   6870         }
   6871 
   6872         mLastVelX = vx;
   6873         mLastVelY = vy;
   6874         mLastVelocity = velocity;
   6875 
   6876         // no horizontal overscroll if the content just fits
   6877         mScroller.fling(scrollX, scrollY, -vx, -vy, 0, maxX, 0, maxY,
   6878                 maxX == 0 ? 0 : overflingDistance, overflingDistance);
   6879 
   6880         invalidate();
   6881     }
   6882 
   6883     /**
   6884      * See {@link WebView#getZoomControls()}
   6885      */
   6886     @Override
   6887     @Deprecated
   6888     public View getZoomControls() {
   6889         if (!getSettings().supportZoom()) {
   6890             Log.w(LOGTAG, "This WebView doesn't support zoom.");
   6891             return null;
   6892         }
   6893         return mZoomManager.getExternalZoomPicker();
   6894     }
   6895 
   6896     void dismissZoomControl() {
   6897         mZoomManager.dismissZoomPicker();
   6898     }
   6899 
   6900     float getDefaultZoomScale() {
   6901         return mZoomManager.getDefaultScale();
   6902     }
   6903 
   6904     /**
   6905      * Return the overview scale of the WebView
   6906      * @return The overview scale.
   6907      */
   6908     float getZoomOverviewScale() {
   6909         return mZoomManager.getZoomOverviewScale();
   6910     }
   6911 
   6912     /**
   6913      * See {@link WebView#canZoomIn()}
   6914      */
   6915     @Override
   6916     public boolean canZoomIn() {
   6917         return mZoomManager.canZoomIn();
   6918     }
   6919 
   6920     /**
   6921      * See {@link WebView#canZoomOut()}
   6922      */
   6923     @Override
   6924     public boolean canZoomOut() {
   6925         return mZoomManager.canZoomOut();
   6926     }
   6927 
   6928     /**
   6929      * See {@link WebView#zoomIn()}
   6930      */
   6931     @Override
   6932     public boolean zoomIn() {
   6933         return mZoomManager.zoomIn();
   6934     }
   6935 
   6936     /**
   6937      * See {@link WebView#zoomOut()}
   6938      */
   6939     @Override
   6940     public boolean zoomOut() {
   6941         return mZoomManager.zoomOut();
   6942     }
   6943 
   6944     /*
   6945      * Return true if the rect (e.g. plugin) is fully visible and maximized
   6946      * inside the WebView.
   6947      */
   6948     boolean isRectFitOnScreen(Rect rect) {
   6949         final int rectWidth = rect.width();
   6950         final int rectHeight = rect.height();
   6951         final int viewWidth = getViewWidth();
   6952         final int viewHeight = getViewHeightWithTitle();
   6953         float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight / rectHeight);
   6954         scale = mZoomManager.computeScaleWithLimits(scale);
   6955         return !mZoomManager.willScaleTriggerZoom(scale)
   6956                 && contentToViewX(rect.left) >= getScrollX()
   6957                 && contentToViewX(rect.right) <= getScrollX() + viewWidth
   6958                 && contentToViewY(rect.top) >= getScrollY()
   6959                 && contentToViewY(rect.bottom) <= getScrollY() + viewHeight;
   6960     }
   6961 
   6962     /*
   6963      * Maximize and center the rectangle, specified in the document coordinate
   6964      * space, inside the WebView. If the zoom doesn't need to be changed, do an
   6965      * animated scroll to center it. If the zoom needs to be changed, find the
   6966      * zoom center and do a smooth zoom transition. The rect is in document
   6967      * coordinates
   6968      */
   6969     void centerFitRect(Rect rect) {
   6970         final int rectWidth = rect.width();
   6971         final int rectHeight = rect.height();
   6972         final int viewWidth = getViewWidth();
   6973         final int viewHeight = getViewHeightWithTitle();
   6974         float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight
   6975                 / rectHeight);
   6976         scale = mZoomManager.computeScaleWithLimits(scale);
   6977         if (!mZoomManager.willScaleTriggerZoom(scale)) {
   6978             pinScrollTo(contentToViewX(rect.left + rectWidth / 2) - viewWidth / 2,
   6979                     contentToViewY(rect.top + rectHeight / 2) - viewHeight / 2,
   6980                     true, 0);
   6981         } else {
   6982             float actualScale = mZoomManager.getScale();
   6983             float oldScreenX = rect.left * actualScale - getScrollX();
   6984             float rectViewX = rect.left * scale;
   6985             float rectViewWidth = rectWidth * scale;
   6986             float newMaxWidth = mContentWidth * scale;
   6987             float newScreenX = (viewWidth - rectViewWidth) / 2;
   6988             // pin the newX to the WebView
   6989             if (newScreenX > rectViewX) {
   6990                 newScreenX = rectViewX;
   6991             } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) {
   6992                 newScreenX = viewWidth - (newMaxWidth - rectViewX);
   6993             }
   6994             float zoomCenterX = (oldScreenX * scale - newScreenX * actualScale)
   6995                     / (scale - actualScale);
   6996             float oldScreenY = rect.top * actualScale + getTitleHeight()
   6997                     - getScrollY();
   6998             float rectViewY = rect.top * scale + getTitleHeight();
   6999             float rectViewHeight = rectHeight * scale;
   7000             float newMaxHeight = mContentHeight * scale + getTitleHeight();
   7001             float newScreenY = (viewHeight - rectViewHeight) / 2;
   7002             // pin the newY to the WebView
   7003             if (newScreenY > rectViewY) {
   7004                 newScreenY = rectViewY;
   7005             } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) {
   7006                 newScreenY = viewHeight - (newMaxHeight - rectViewY);
   7007             }
   7008             float zoomCenterY = (oldScreenY * scale - newScreenY * actualScale)
   7009                     / (scale - actualScale);
   7010             mZoomManager.setZoomCenter(zoomCenterX, zoomCenterY);
   7011             mZoomManager.startZoomAnimation(scale, false);
   7012         }
   7013     }
   7014 
   7015     // Called by JNI to handle a touch on a node representing an email address,
   7016     // address, or phone number
   7017     private void overrideLoading(String url) {
   7018         mCallbackProxy.uiOverrideUrlLoading(url);
   7019     }
   7020 
   7021     @Override
   7022     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
   7023         // Check if we are destroyed
   7024         if (mWebViewCore == null) return false;
   7025         // FIXME: If a subwindow is showing find, and the user touches the
   7026         // background window, it can steal focus.
   7027         if (mFindIsUp) return false;
   7028         boolean result = false;
   7029         result = mWebViewPrivate.super_requestFocus(direction, previouslyFocusedRect);
   7030         if (mWebViewCore.getSettings().getNeedInitialFocus()
   7031                 && !mWebView.isInTouchMode()) {
   7032             // For cases such as GMail, where we gain focus from a direction,
   7033             // we want to move to the first available link.
   7034             // FIXME: If there are no visible links, we may not want to
   7035             int fakeKeyDirection = 0;
   7036             switch(direction) {
   7037                 case View.FOCUS_UP:
   7038                     fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
   7039                     break;
   7040                 case View.FOCUS_DOWN:
   7041                     fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
   7042                     break;
   7043                 case View.FOCUS_LEFT:
   7044                     fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
   7045                     break;
   7046                 case View.FOCUS_RIGHT:
   7047                     fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
   7048                     break;
   7049                 default:
   7050                     return result;
   7051             }
   7052             mWebViewCore.sendMessage(EventHub.SET_INITIAL_FOCUS, fakeKeyDirection);
   7053         }
   7054         return result;
   7055     }
   7056 
   7057     @Override
   7058     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   7059         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
   7060         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
   7061         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
   7062         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
   7063 
   7064         int measuredHeight = heightSize;
   7065         int measuredWidth = widthSize;
   7066 
   7067         // Grab the content size from WebViewCore.
   7068         int contentHeight = contentToViewDimension(mContentHeight);
   7069         int contentWidth = contentToViewDimension(mContentWidth);
   7070 
   7071 //        Log.d(LOGTAG, "------- measure " + heightMode);
   7072 
   7073         if (heightMode != MeasureSpec.EXACTLY) {
   7074             mHeightCanMeasure = true;
   7075             measuredHeight = contentHeight;
   7076             if (heightMode == MeasureSpec.AT_MOST) {
   7077                 // If we are larger than the AT_MOST height, then our height can
   7078                 // no longer be measured and we should scroll internally.
   7079                 if (measuredHeight > heightSize) {
   7080                     measuredHeight = heightSize;
   7081                     mHeightCanMeasure = false;
   7082                     measuredHeight |= View.MEASURED_STATE_TOO_SMALL;
   7083                 }
   7084             }
   7085         } else {
   7086             mHeightCanMeasure = false;
   7087         }
   7088         if (mNativeClass != 0) {
   7089             nativeSetHeightCanMeasure(mHeightCanMeasure);
   7090         }
   7091         // For the width, always use the given size unless unspecified.
   7092         if (widthMode == MeasureSpec.UNSPECIFIED) {
   7093             mWidthCanMeasure = true;
   7094             measuredWidth = contentWidth;
   7095         } else {
   7096             if (measuredWidth < contentWidth) {
   7097                 measuredWidth |= View.MEASURED_STATE_TOO_SMALL;
   7098             }
   7099             mWidthCanMeasure = false;
   7100         }
   7101 
   7102         synchronized (this) {
   7103             mWebViewPrivate.setMeasuredDimension(measuredWidth, measuredHeight);
   7104         }
   7105     }
   7106 
   7107     @Override
   7108     public boolean requestChildRectangleOnScreen(View child,
   7109                                                  Rect rect,
   7110                                                  boolean immediate) {
   7111         if (mNativeClass == 0) {
   7112             return false;
   7113         }
   7114         // don't scroll while in zoom animation. When it is done, we will adjust
   7115         // the necessary components
   7116         if (mZoomManager.isFixedLengthAnimationInProgress()) {
   7117             return false;
   7118         }
   7119 
   7120         rect.offset(child.getLeft() - child.getScrollX(),
   7121                 child.getTop() - child.getScrollY());
   7122 
   7123         Rect content = new Rect(viewToContentX(getScrollX()),
   7124                 viewToContentY(getScrollY()),
   7125                 viewToContentX(getScrollX() + getWidth()
   7126                 - mWebView.getVerticalScrollbarWidth()),
   7127                 viewToContentY(getScrollY() + getViewHeightWithTitle()));
   7128         int screenTop = contentToViewY(content.top);
   7129         int screenBottom = contentToViewY(content.bottom);
   7130         int height = screenBottom - screenTop;
   7131         int scrollYDelta = 0;
   7132 
   7133         if (rect.bottom > screenBottom) {
   7134             int oneThirdOfScreenHeight = height / 3;
   7135             if (rect.height() > 2 * oneThirdOfScreenHeight) {
   7136                 // If the rectangle is too tall to fit in the bottom two thirds
   7137                 // of the screen, place it at the top.
   7138                 scrollYDelta = rect.top - screenTop;
   7139             } else {
   7140                 // If the rectangle will still fit on screen, we want its
   7141                 // top to be in the top third of the screen.
   7142                 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
   7143             }
   7144         } else if (rect.top < screenTop) {
   7145             scrollYDelta = rect.top - screenTop;
   7146         }
   7147 
   7148         int screenLeft = contentToViewX(content.left);
   7149         int screenRight = contentToViewX(content.right);
   7150         int width = screenRight - screenLeft;
   7151         int scrollXDelta = 0;
   7152 
   7153         if (rect.right > screenRight && rect.left > screenLeft) {
   7154             if (rect.width() > width) {
   7155                 scrollXDelta += (rect.left - screenLeft);
   7156             } else {
   7157                 scrollXDelta += (rect.right - screenRight);
   7158             }
   7159         } else if (rect.left < screenLeft) {
   7160             scrollXDelta -= (screenLeft - rect.left);
   7161         }
   7162 
   7163         if ((scrollYDelta | scrollXDelta) != 0) {
   7164             return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
   7165         }
   7166 
   7167         return false;
   7168     }
   7169 
   7170     /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
   7171             String replace, int newStart, int newEnd) {
   7172         WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
   7173         arg.mReplace = replace;
   7174         arg.mNewStart = newStart;
   7175         arg.mNewEnd = newEnd;
   7176         mTextGeneration++;
   7177         arg.mTextGeneration = mTextGeneration;
   7178         sendBatchableInputMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
   7179     }
   7180 
   7181     /* package */ void passToJavaScript(String currentText, KeyEvent event) {
   7182         // check if mWebViewCore has been destroyed
   7183         if (mWebViewCore == null) {
   7184             return;
   7185         }
   7186         WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
   7187         arg.mEvent = event;
   7188         arg.mCurrentText = currentText;
   7189         // Increase our text generation number, and pass it to webcore thread
   7190         mTextGeneration++;
   7191         mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
   7192         // WebKit's document state is not saved until about to leave the page.
   7193         // To make sure the host application, like Browser, has the up to date
   7194         // document state when it goes to background, we force to save the
   7195         // document state.
   7196         mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
   7197         mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE, null, 1000);
   7198     }
   7199 
   7200     public synchronized WebViewCore getWebViewCore() {
   7201         return mWebViewCore;
   7202     }
   7203 
   7204     private boolean canTextScroll(int directionX, int directionY) {
   7205         int scrollX = getTextScrollX();
   7206         int scrollY = getTextScrollY();
   7207         int maxScrollX = getMaxTextScrollX();
   7208         int maxScrollY = getMaxTextScrollY();
   7209         boolean canScrollX = (directionX > 0)
   7210                 ? (scrollX < maxScrollX)
   7211                 : (scrollX > 0);
   7212         boolean canScrollY = (directionY > 0)
   7213                 ? (scrollY < maxScrollY)
   7214                 : (scrollY > 0);
   7215         return canScrollX || canScrollY;
   7216     }
   7217 
   7218     private int getTextScrollX() {
   7219         return -mEditTextContent.left;
   7220     }
   7221 
   7222     private int getTextScrollY() {
   7223         return -mEditTextContent.top;
   7224     }
   7225 
   7226     private int getMaxTextScrollX() {
   7227         return Math.max(0, mEditTextContent.width() - mEditTextContentBounds.width());
   7228     }
   7229 
   7230     private int getMaxTextScrollY() {
   7231         return Math.max(0, mEditTextContent.height() - mEditTextContentBounds.height());
   7232     }
   7233 
   7234     //-------------------------------------------------------------------------
   7235     // Methods can be called from a separate thread, like WebViewCore
   7236     // If it needs to call the View system, it has to send message.
   7237     //-------------------------------------------------------------------------
   7238 
   7239     /**
   7240      * General handler to receive message coming from webkit thread
   7241      */
   7242     class PrivateHandler extends Handler implements WebViewInputDispatcher.UiCallbacks {
   7243         @Override
   7244         public void handleMessage(Message msg) {
   7245             // exclude INVAL_RECT_MSG_ID since it is frequently output
   7246             if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
   7247                 if (msg.what >= FIRST_PRIVATE_MSG_ID
   7248                         && msg.what <= LAST_PRIVATE_MSG_ID) {
   7249                     Log.v(LOGTAG, HandlerPrivateDebugString[msg.what
   7250                             - FIRST_PRIVATE_MSG_ID]);
   7251                 } else if (msg.what >= FIRST_PACKAGE_MSG_ID
   7252                         && msg.what <= LAST_PACKAGE_MSG_ID) {
   7253                     Log.v(LOGTAG, HandlerPackageDebugString[msg.what
   7254                             - FIRST_PACKAGE_MSG_ID]);
   7255                 } else {
   7256                     Log.v(LOGTAG, Integer.toString(msg.what));
   7257                 }
   7258             }
   7259             if (mWebViewCore == null) {
   7260                 // after WebView's destroy() is called, skip handling messages.
   7261                 return;
   7262             }
   7263             if (mBlockWebkitViewMessages
   7264                     && msg.what != WEBCORE_INITIALIZED_MSG_ID) {
   7265                 // Blocking messages from webkit
   7266                 return;
   7267             }
   7268             switch (msg.what) {
   7269                 case REMEMBER_PASSWORD: {
   7270                     mDatabase.setUsernamePassword(
   7271                             msg.getData().getString("host"),
   7272                             msg.getData().getString("username"),
   7273                             msg.getData().getString("password"));
   7274                     ((Message) msg.obj).sendToTarget();
   7275                     break;
   7276                 }
   7277                 case NEVER_REMEMBER_PASSWORD: {
   7278                     mDatabase.setUsernamePassword(msg.getData().getString("host"), null, null);
   7279                     ((Message) msg.obj).sendToTarget();
   7280                     break;
   7281                 }
   7282                 case SCROLL_SELECT_TEXT: {
   7283                     if (mAutoScrollX == 0 && mAutoScrollY == 0) {
   7284                         mSentAutoScrollMessage = false;
   7285                         break;
   7286                     }
   7287                     if (mCurrentScrollingLayerId == 0) {
   7288                         pinScrollBy(mAutoScrollX, mAutoScrollY, true, 0);
   7289                     } else {
   7290                         scrollLayerTo(mScrollingLayerRect.left + mAutoScrollX,
   7291                                 mScrollingLayerRect.top + mAutoScrollY);
   7292                     }
   7293                     sendEmptyMessageDelayed(
   7294                             SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL);
   7295                     break;
   7296                 }
   7297                 case SCROLL_TO_MSG_ID: {
   7298                     // arg1 = animate, arg2 = onlyIfImeIsShowing
   7299                     // obj = Point(x, y)
   7300                     if (msg.arg2 == 1) {
   7301                         // This scroll is intended to bring the textfield into
   7302                         // view, but is only necessary if the IME is showing
   7303                         InputMethodManager imm = InputMethodManager.peekInstance();
   7304                         if (imm == null || !imm.isAcceptingText()
   7305                                 || !imm.isActive(mWebView)) {
   7306                             break;
   7307                         }
   7308                     }
   7309                     final Point p = (Point) msg.obj;
   7310                     contentScrollTo(p.x, p.y, msg.arg1 == 1);
   7311                     break;
   7312                 }
   7313                 case UPDATE_ZOOM_RANGE: {
   7314                     WebViewCore.ViewState viewState = (WebViewCore.ViewState) msg.obj;
   7315                     // mScrollX contains the new minPrefWidth
   7316                     mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
   7317                     break;
   7318                 }
   7319                 case UPDATE_ZOOM_DENSITY: {
   7320                     final float density = (Float) msg.obj;
   7321                     mZoomManager.updateDefaultZoomDensity(density);
   7322                     break;
   7323                 }
   7324                 case NEW_PICTURE_MSG_ID: {
   7325                     // called for new content
   7326                     final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
   7327                     setNewPicture(draw, true);
   7328                     break;
   7329                 }
   7330                 case WEBCORE_INITIALIZED_MSG_ID:
   7331                     // nativeCreate sets mNativeClass to a non-zero value
   7332                     String drawableDir = BrowserFrame.getRawResFilename(
   7333                             BrowserFrame.DRAWABLEDIR, mContext);
   7334                     nativeCreate(msg.arg1, drawableDir, ActivityManager.isHighEndGfx());
   7335                     if (mDelaySetPicture != null) {
   7336                         setNewPicture(mDelaySetPicture, true);
   7337                         mDelaySetPicture = null;
   7338                     }
   7339                     if (mIsPaused) {
   7340                         nativeSetPauseDrawing(mNativeClass, true);
   7341                     }
   7342                     mInputDispatcher = new WebViewInputDispatcher(this,
   7343                             mWebViewCore.getInputDispatcherCallbacks());
   7344                     break;
   7345                 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
   7346                     // Make sure that the textfield is currently focused
   7347                     // and representing the same node as the pointer.
   7348                     if (msg.arg2 == mTextGeneration) {
   7349                         String text = (String) msg.obj;
   7350                         if (null == text) {
   7351                             text = "";
   7352                         }
   7353                         if (mInputConnection != null &&
   7354                                 mFieldPointer == msg.arg1) {
   7355                             mInputConnection.setTextAndKeepSelection(text);
   7356                         }
   7357                     }
   7358                     break;
   7359                 case UPDATE_TEXT_SELECTION_MSG_ID:
   7360                     updateTextSelectionFromMessage(msg.arg1, msg.arg2,
   7361                             (WebViewCore.TextSelectionData) msg.obj);
   7362                     break;
   7363                 case TAKE_FOCUS:
   7364                     int direction = msg.arg1;
   7365                     View focusSearch = mWebView.focusSearch(direction);
   7366                     if (focusSearch != null && focusSearch != mWebView) {
   7367                         focusSearch.requestFocus();
   7368                     }
   7369                     break;
   7370                 case CLEAR_TEXT_ENTRY:
   7371                     hideSoftKeyboard();
   7372                     break;
   7373                 case INVAL_RECT_MSG_ID: {
   7374                     Rect r = (Rect)msg.obj;
   7375                     if (r == null) {
   7376                         invalidate();
   7377                     } else {
   7378                         // we need to scale r from content into view coords,
   7379                         // which viewInvalidate() does for us
   7380                         viewInvalidate(r.left, r.top, r.right, r.bottom);
   7381                     }
   7382                     break;
   7383                 }
   7384                 case REQUEST_FORM_DATA:
   7385                     if (mFieldPointer == msg.arg1) {
   7386                         ArrayAdapter<String> adapter = (ArrayAdapter<String>)msg.obj;
   7387                         mAutoCompletePopup.setAdapter(adapter);
   7388                     }
   7389                     break;
   7390 
   7391                 case LONG_PRESS_CENTER:
   7392                     // as this is shared by keydown and trackballdown, reset all
   7393                     // the states
   7394                     mGotCenterDown = false;
   7395                     mTrackballDown = false;
   7396                     mWebView.performLongClick();
   7397                     break;
   7398 
   7399                 case WEBCORE_NEED_TOUCH_EVENTS:
   7400                     mInputDispatcher.setWebKitWantsTouchEvents(msg.arg1 != 0);
   7401                     break;
   7402 
   7403                 case REQUEST_KEYBOARD:
   7404                     if (msg.arg1 == 0) {
   7405                         hideSoftKeyboard();
   7406                     } else {
   7407                         displaySoftKeyboard(false);
   7408                     }
   7409                     break;
   7410 
   7411                 case DRAG_HELD_MOTIONLESS:
   7412                     mHeldMotionless = MOTIONLESS_TRUE;
   7413                     invalidate();
   7414                     break;
   7415 
   7416                 case SCREEN_ON:
   7417                     mWebView.setKeepScreenOn(msg.arg1 == 1);
   7418                     break;
   7419 
   7420                 case EXIT_FULLSCREEN_VIDEO:
   7421                     if (mHTML5VideoViewProxy != null) {
   7422                         mHTML5VideoViewProxy.exitFullScreenVideo();
   7423                     }
   7424                     break;
   7425 
   7426                 case SHOW_FULLSCREEN: {
   7427                     View view = (View) msg.obj;
   7428                     int orientation = msg.arg1;
   7429                     int npp = msg.arg2;
   7430 
   7431                     if (inFullScreenMode()) {
   7432                         Log.w(LOGTAG, "Should not have another full screen.");
   7433                         dismissFullScreenMode();
   7434                     }
   7435                     mFullScreenHolder = new PluginFullScreenHolder(WebViewClassic.this, orientation, npp);
   7436                     mFullScreenHolder.setContentView(view);
   7437                     mFullScreenHolder.show();
   7438                     invalidate();
   7439 
   7440                     break;
   7441                 }
   7442                 case HIDE_FULLSCREEN:
   7443                     dismissFullScreenMode();
   7444                     break;
   7445 
   7446                 case SHOW_RECT_MSG_ID: {
   7447                     WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
   7448                     int left = contentToViewX(data.mLeft);
   7449                     int width = contentToViewDimension(data.mWidth);
   7450                     int maxWidth = contentToViewDimension(data.mContentWidth);
   7451                     int viewWidth = getViewWidth();
   7452                     int x = (int) (left + data.mXPercentInDoc * width -
   7453                                    data.mXPercentInView * viewWidth);
   7454                     if (DebugFlags.WEB_VIEW) {
   7455                         Log.v(LOGTAG, "showRectMsg=(left=" + left + ",width=" +
   7456                               width + ",maxWidth=" + maxWidth +
   7457                               ",viewWidth=" + viewWidth + ",x="
   7458                               + x + ",xPercentInDoc=" + data.mXPercentInDoc +
   7459                               ",xPercentInView=" + data.mXPercentInView+ ")");
   7460                     }
   7461                     // use the passing content width to cap x as the current
   7462                     // mContentWidth may not be updated yet
   7463                     x = Math.max(0,
   7464                             (Math.min(maxWidth, x + viewWidth)) - viewWidth);
   7465                     int top = contentToViewY(data.mTop);
   7466                     int height = contentToViewDimension(data.mHeight);
   7467                     int maxHeight = contentToViewDimension(data.mContentHeight);
   7468                     int viewHeight = getViewHeight();
   7469                     int y = (int) (top + data.mYPercentInDoc * height -
   7470                                    data.mYPercentInView * viewHeight);
   7471                     if (DebugFlags.WEB_VIEW) {
   7472                         Log.v(LOGTAG, "showRectMsg=(top=" + top + ",height=" +
   7473                               height + ",maxHeight=" + maxHeight +
   7474                               ",viewHeight=" + viewHeight + ",y="
   7475                               + y + ",yPercentInDoc=" + data.mYPercentInDoc +
   7476                               ",yPercentInView=" + data.mYPercentInView+ ")");
   7477                     }
   7478                     // use the passing content height to cap y as the current
   7479                     // mContentHeight may not be updated yet
   7480                     y = Math.max(0,
   7481                             (Math.min(maxHeight, y + viewHeight) - viewHeight));
   7482                     // We need to take into account the visible title height
   7483                     // when scrolling since y is an absolute view position.
   7484                     y = Math.max(0, y - getVisibleTitleHeightImpl());
   7485                     mWebView.scrollTo(x, y);
   7486                     }
   7487                     break;
   7488 
   7489                 case CENTER_FIT_RECT:
   7490                     centerFitRect((Rect)msg.obj);
   7491                     break;
   7492 
   7493                 case SET_SCROLLBAR_MODES:
   7494                     mHorizontalScrollBarMode = msg.arg1;
   7495                     mVerticalScrollBarMode = msg.arg2;
   7496                     break;
   7497 
   7498                 case SELECTION_STRING_CHANGED:
   7499                     if (isAccessibilityInjectionEnabled()) {
   7500                         getAccessibilityInjector()
   7501                                 .handleSelectionChangedIfNecessary((String) msg.obj);
   7502                     }
   7503                     break;
   7504 
   7505                 case FOCUS_NODE_CHANGED:
   7506                     mIsEditingText = (msg.arg1 == mFieldPointer);
   7507                     if (mAutoCompletePopup != null && !mIsEditingText) {
   7508                         mAutoCompletePopup.clearAdapter();
   7509                     }
   7510                     // fall through to HIT_TEST_RESULT
   7511                 case HIT_TEST_RESULT:
   7512                     WebKitHitTest hit = (WebKitHitTest) msg.obj;
   7513                     mFocusedNode = hit;
   7514                     setTouchHighlightRects(hit);
   7515                     setHitTestResult(hit);
   7516                     break;
   7517 
   7518                 case SAVE_WEBARCHIVE_FINISHED:
   7519                     SaveWebArchiveMessage saveMessage = (SaveWebArchiveMessage)msg.obj;
   7520                     if (saveMessage.mCallback != null) {
   7521                         saveMessage.mCallback.onReceiveValue(saveMessage.mResultFile);
   7522                     }
   7523                     break;
   7524 
   7525                 case SET_AUTOFILLABLE:
   7526                     mAutoFillData = (WebViewCore.AutoFillData) msg.obj;
   7527                     if (mInputConnection != null) {
   7528                         mInputConnection.setAutoFillable(mAutoFillData.getQueryId());
   7529                         mAutoCompletePopup.setAutoFillQueryId(mAutoFillData.getQueryId());
   7530                     }
   7531                     break;
   7532 
   7533                 case AUTOFILL_COMPLETE:
   7534                     if (mAutoCompletePopup != null) {
   7535                         ArrayList<String> pastEntries = new ArrayList<String>();
   7536                         mAutoCompletePopup.setAdapter(new ArrayAdapter<String>(
   7537                                 mContext,
   7538                                 com.android.internal.R.layout.web_text_view_dropdown,
   7539                                 pastEntries));
   7540                     }
   7541                     break;
   7542 
   7543                 case COPY_TO_CLIPBOARD:
   7544                     copyToClipboard((String) msg.obj);
   7545                     break;
   7546 
   7547                 case INIT_EDIT_FIELD:
   7548                     if (mInputConnection != null) {
   7549                         TextFieldInitData initData = (TextFieldInitData) msg.obj;
   7550                         mTextGeneration = 0;
   7551                         mFieldPointer = initData.mFieldPointer;
   7552                         mInputConnection.initEditorInfo(initData);
   7553                         mInputConnection.setTextAndKeepSelection(initData.mText);
   7554                         mEditTextContentBounds.set(initData.mContentBounds);
   7555                         mEditTextLayerId = initData.mNodeLayerId;
   7556                         nativeMapLayerRect(mNativeClass, mEditTextLayerId,
   7557                                 mEditTextContentBounds);
   7558                         mEditTextContent.set(initData.mClientRect);
   7559                         relocateAutoCompletePopup();
   7560                     }
   7561                     break;
   7562 
   7563                 case REPLACE_TEXT:{
   7564                     String text = (String)msg.obj;
   7565                     int start = msg.arg1;
   7566                     int end = msg.arg2;
   7567                     int cursorPosition = start + text.length();
   7568                     replaceTextfieldText(start, end, text,
   7569                             cursorPosition, cursorPosition);
   7570                     selectionDone();
   7571                     break;
   7572                 }
   7573 
   7574                 case UPDATE_MATCH_COUNT: {
   7575                     WebViewCore.FindAllRequest request = (WebViewCore.FindAllRequest)msg.obj;
   7576                     if (request == null) {
   7577                         if (mFindCallback != null) {
   7578                             mFindCallback.updateMatchCount(0, 0, true);
   7579                         }
   7580                     } else if (request == mFindRequest) {
   7581                         int matchCount, matchIndex;
   7582                         synchronized (mFindRequest) {
   7583                             matchCount = request.mMatchCount;
   7584                             matchIndex = request.mMatchIndex;
   7585                         }
   7586                         if (mFindCallback != null) {
   7587                             mFindCallback.updateMatchCount(matchIndex, matchCount, false);
   7588                         }
   7589                         if (mFindListener != null) {
   7590                             mFindListener.onFindResultReceived(matchIndex, matchCount, true);
   7591                         }
   7592                     }
   7593                     break;
   7594                 }
   7595 
   7596                 case CLEAR_CARET_HANDLE:
   7597                     if (mIsCaretSelection) {
   7598                         selectionDone();
   7599                     }
   7600                     break;
   7601 
   7602                 case KEY_PRESS:
   7603                     sendBatchableInputMessage(EventHub.KEY_PRESS, msg.arg1, 0, null);
   7604                     break;
   7605 
   7606                 case RELOCATE_AUTO_COMPLETE_POPUP:
   7607                     relocateAutoCompletePopup();
   7608                     break;
   7609 
   7610                 case AUTOFILL_FORM:
   7611                     mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM,
   7612                             msg.arg1, /* unused */0);
   7613                     break;
   7614 
   7615                 case EDIT_TEXT_SIZE_CHANGED:
   7616                     if (msg.arg1 == mFieldPointer) {
   7617                         mEditTextContent.set((Rect)msg.obj);
   7618                     }
   7619                     break;
   7620 
   7621                 case SHOW_CARET_HANDLE:
   7622                     if (!mSelectingText && mIsEditingText && mIsCaretSelection) {
   7623                         setupWebkitSelect();
   7624                         resetCaretTimer();
   7625                         showPasteWindow();
   7626                     }
   7627                     break;
   7628 
   7629                 case UPDATE_CONTENT_BOUNDS:
   7630                     mEditTextContentBounds.set((Rect) msg.obj);
   7631                     nativeMapLayerRect(mNativeClass, mEditTextLayerId,
   7632                             mEditTextContentBounds);
   7633                     break;
   7634 
   7635                 case SCROLL_EDIT_TEXT:
   7636                     scrollEditWithCursor();
   7637                     break;
   7638 
   7639                 case SCROLL_HANDLE_INTO_VIEW:
   7640                     scrollDraggedSelectionHandleIntoView();
   7641                     break;
   7642 
   7643                 default:
   7644                     super.handleMessage(msg);
   7645                     break;
   7646             }
   7647         }
   7648 
   7649         @Override
   7650         public Looper getUiLooper() {
   7651             return getLooper();
   7652         }
   7653 
   7654         @Override
   7655         public void dispatchUiEvent(MotionEvent event, int eventType, int flags) {
   7656             onHandleUiEvent(event, eventType, flags);
   7657         }
   7658 
   7659         @Override
   7660         public Context getContext() {
   7661             return WebViewClassic.this.getContext();
   7662         }
   7663 
   7664         @Override
   7665         public boolean shouldInterceptTouchEvent(MotionEvent event) {
   7666             if (!mSelectingText) {
   7667                 return false;
   7668             }
   7669             ensureSelectionHandles();
   7670             int y = Math.round(event.getY() - getTitleHeight() + getScrollY());
   7671             int x = Math.round(event.getX() + getScrollX());
   7672             boolean isPressingHandle;
   7673             if (mIsCaretSelection) {
   7674                 isPressingHandle = mSelectHandleCenter.getBounds()
   7675                         .contains(x, y);
   7676             } else {
   7677                 isPressingHandle =
   7678                         mSelectHandleBaseBounds.contains(x, y)
   7679                         || mSelectHandleExtentBounds.contains(x, y);
   7680             }
   7681             return isPressingHandle;
   7682         }
   7683 
   7684         @Override
   7685         public void showTapHighlight(boolean show) {
   7686             if (mShowTapHighlight != show) {
   7687                 mShowTapHighlight = show;
   7688                 invalidate();
   7689             }
   7690         }
   7691 
   7692         @Override
   7693         public void clearPreviousHitTest() {
   7694             setHitTestResult(null);
   7695         }
   7696     }
   7697 
   7698     private void setHitTestTypeFromUrl(String url) {
   7699         String substr = null;
   7700         if (url.startsWith(SCHEME_GEO)) {
   7701             mInitialHitTestResult.setType(HitTestResult.GEO_TYPE);
   7702             substr = url.substring(SCHEME_GEO.length());
   7703         } else if (url.startsWith(SCHEME_TEL)) {
   7704             mInitialHitTestResult.setType(HitTestResult.PHONE_TYPE);
   7705             substr = url.substring(SCHEME_TEL.length());
   7706         } else if (url.startsWith(SCHEME_MAILTO)) {
   7707             mInitialHitTestResult.setType(HitTestResult.EMAIL_TYPE);
   7708             substr = url.substring(SCHEME_MAILTO.length());
   7709         } else {
   7710             mInitialHitTestResult.setType(HitTestResult.SRC_ANCHOR_TYPE);
   7711             mInitialHitTestResult.setExtra(url);
   7712             return;
   7713         }
   7714         try {
   7715             mInitialHitTestResult.setExtra(URLDecoder.decode(substr, "UTF-8"));
   7716         } catch (Throwable e) {
   7717             Log.w(LOGTAG, "Failed to decode URL! " + substr, e);
   7718             mInitialHitTestResult.setType(HitTestResult.UNKNOWN_TYPE);
   7719         }
   7720     }
   7721 
   7722     private void setHitTestResult(WebKitHitTest hit) {
   7723         if (hit == null) {
   7724             mInitialHitTestResult = null;
   7725             return;
   7726         }
   7727         mInitialHitTestResult = new HitTestResult();
   7728         if (hit.mLinkUrl != null) {
   7729             setHitTestTypeFromUrl(hit.mLinkUrl);
   7730             if (hit.mImageUrl != null
   7731                     && mInitialHitTestResult.getType() == HitTestResult.SRC_ANCHOR_TYPE) {
   7732                 mInitialHitTestResult.setType(HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
   7733                 mInitialHitTestResult.setExtra(hit.mImageUrl);
   7734             }
   7735         } else if (hit.mImageUrl != null) {
   7736             mInitialHitTestResult.setType(HitTestResult.IMAGE_TYPE);
   7737             mInitialHitTestResult.setExtra(hit.mImageUrl);
   7738         } else if (hit.mEditable) {
   7739             mInitialHitTestResult.setType(HitTestResult.EDIT_TEXT_TYPE);
   7740         } else if (hit.mIntentUrl != null) {
   7741             setHitTestTypeFromUrl(hit.mIntentUrl);
   7742         }
   7743     }
   7744 
   7745     private boolean shouldDrawHighlightRect() {
   7746         if (mFocusedNode == null || mInitialHitTestResult == null) {
   7747             return false;
   7748         }
   7749         if (mTouchHighlightRegion.isEmpty()) {
   7750             return false;
   7751         }
   7752         if (mFocusedNode.mHasFocus && !mWebView.isInTouchMode()) {
   7753             return mDrawCursorRing && !mFocusedNode.mEditable;
   7754         }
   7755         if (mFocusedNode.mHasFocus && mFocusedNode.mEditable) {
   7756             return false;
   7757         }
   7758         return mShowTapHighlight;
   7759     }
   7760 
   7761 
   7762     private FocusTransitionDrawable mFocusTransition = null;
   7763     static class FocusTransitionDrawable extends Drawable {
   7764         Region mPreviousRegion;
   7765         Region mNewRegion;
   7766         float mProgress = 0;
   7767         WebViewClassic mWebView;
   7768         Paint mPaint;
   7769         int mMaxAlpha;
   7770         Point mTranslate;
   7771 
   7772         public FocusTransitionDrawable(WebViewClassic view) {
   7773             mWebView = view;
   7774             mPaint = new Paint(mWebView.mTouchHightlightPaint);
   7775             mMaxAlpha = mPaint.getAlpha();
   7776         }
   7777 
   7778         @Override
   7779         public void setColorFilter(ColorFilter cf) {
   7780         }
   7781 
   7782         @Override
   7783         public void setAlpha(int alpha) {
   7784         }
   7785 
   7786         @Override
   7787         public int getOpacity() {
   7788             return 0;
   7789         }
   7790 
   7791         public void setProgress(float p) {
   7792             mProgress = p;
   7793             if (mWebView.mFocusTransition == this) {
   7794                 if (mProgress == 1f)
   7795                     mWebView.mFocusTransition = null;
   7796                 mWebView.invalidate();
   7797             }
   7798         }
   7799 
   7800         public float getProgress() {
   7801             return mProgress;
   7802         }
   7803 
   7804         @Override
   7805         public void draw(Canvas canvas) {
   7806             if (mTranslate == null) {
   7807                 Rect bounds = mPreviousRegion.getBounds();
   7808                 Point from = new Point(bounds.centerX(), bounds.centerY());
   7809                 mNewRegion.getBounds(bounds);
   7810                 Point to = new Point(bounds.centerX(), bounds.centerY());
   7811                 mTranslate = new Point(from.x - to.x, from.y - to.y);
   7812             }
   7813             int alpha = (int) (mProgress * mMaxAlpha);
   7814             RegionIterator iter = new RegionIterator(mPreviousRegion);
   7815             Rect r = new Rect();
   7816             mPaint.setAlpha(mMaxAlpha - alpha);
   7817             float tx = mTranslate.x * mProgress;
   7818             float ty = mTranslate.y * mProgress;
   7819             int save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
   7820             canvas.translate(-tx, -ty);
   7821             while (iter.next(r)) {
   7822                 canvas.drawRect(r, mPaint);
   7823             }
   7824             canvas.restoreToCount(save);
   7825             iter = new RegionIterator(mNewRegion);
   7826             r = new Rect();
   7827             mPaint.setAlpha(alpha);
   7828             save = canvas.save(Canvas.MATRIX_SAVE_FLAG);
   7829             tx = mTranslate.x - tx;
   7830             ty = mTranslate.y - ty;
   7831             canvas.translate(tx, ty);
   7832             while (iter.next(r)) {
   7833                 canvas.drawRect(r, mPaint);
   7834             }
   7835             canvas.restoreToCount(save);
   7836         }
   7837     };
   7838 
   7839     private boolean shouldAnimateTo(WebKitHitTest hit) {
   7840         // TODO: Don't be annoying or throw out the animation entirely
   7841         return false;
   7842     }
   7843 
   7844     private void setTouchHighlightRects(WebKitHitTest hit) {
   7845         FocusTransitionDrawable transition = null;
   7846         if (shouldAnimateTo(hit)) {
   7847             transition = new FocusTransitionDrawable(this);
   7848         }
   7849         Rect[] rects = hit != null ? hit.mTouchRects : null;
   7850         if (!mTouchHighlightRegion.isEmpty()) {
   7851             mWebView.invalidate(mTouchHighlightRegion.getBounds());
   7852             if (transition != null) {
   7853                 transition.mPreviousRegion = new Region(mTouchHighlightRegion);
   7854             }
   7855             mTouchHighlightRegion.setEmpty();
   7856         }
   7857         if (rects != null) {
   7858             mTouchHightlightPaint.setColor(hit.mTapHighlightColor);
   7859             for (Rect rect : rects) {
   7860                 Rect viewRect = contentToViewRect(rect);
   7861                 // some sites, like stories in nytimes.com, set
   7862                 // mouse event handler in the top div. It is not
   7863                 // user friendly to highlight the div if it covers
   7864                 // more than half of the screen.
   7865                 if (viewRect.width() < getWidth() >> 1
   7866                         || viewRect.height() < getHeight() >> 1) {
   7867                     mTouchHighlightRegion.union(viewRect);
   7868                 } else if (DebugFlags.WEB_VIEW) {
   7869                     Log.d(LOGTAG, "Skip the huge selection rect:"
   7870                             + viewRect);
   7871                 }
   7872             }
   7873             mWebView.invalidate(mTouchHighlightRegion.getBounds());
   7874             if (transition != null && transition.mPreviousRegion != null) {
   7875                 transition.mNewRegion = new Region(mTouchHighlightRegion);
   7876                 mFocusTransition = transition;
   7877                 ObjectAnimator animator = ObjectAnimator.ofFloat(
   7878                         mFocusTransition, "progress", 1f);
   7879                 animator.start();
   7880             }
   7881         }
   7882     }
   7883 
   7884     // Interface to allow the profiled WebView to hook the page swap notifications.
   7885     public interface PageSwapDelegate {
   7886         void onPageSwapOccurred(boolean notifyAnimationStarted);
   7887     }
   7888 
   7889     long mLastSwapTime;
   7890     double mAverageSwapFps;
   7891 
   7892     /** Called by JNI when pages are swapped (only occurs with hardware
   7893      * acceleration) */
   7894     protected void pageSwapCallback(boolean notifyAnimationStarted) {
   7895         if (DebugFlags.MEASURE_PAGE_SWAP_FPS) {
   7896             long now = System.currentTimeMillis();
   7897             long diff = now - mLastSwapTime;
   7898             mAverageSwapFps = ((1000.0 / diff) + mAverageSwapFps) / 2;
   7899             Log.d(LOGTAG, "page swap fps: " + mAverageSwapFps);
   7900             mLastSwapTime = now;
   7901         }
   7902         mWebViewCore.resumeWebKitDraw();
   7903         if (notifyAnimationStarted) {
   7904             mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED);
   7905         }
   7906         if (mWebView instanceof PageSwapDelegate) {
   7907             // This provides a hook for ProfiledWebView to observe the tile page swaps.
   7908             ((PageSwapDelegate) mWebView).onPageSwapOccurred(notifyAnimationStarted);
   7909         }
   7910 
   7911         if (mPictureListener != null) {
   7912             // trigger picture listener for hardware layers. Software layers are
   7913             // triggered in setNewPicture
   7914             mPictureListener.onNewPicture(getWebView(), capturePicture());
   7915         }
   7916     }
   7917 
   7918     void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) {
   7919         if (mNativeClass == 0) {
   7920             if (mDelaySetPicture != null) {
   7921                 throw new IllegalStateException("Tried to setNewPicture with"
   7922                         + " a delay picture already set! (memory leak)");
   7923             }
   7924             // Not initialized yet, delay set
   7925             mDelaySetPicture = draw;
   7926             return;
   7927         }
   7928         WebViewCore.ViewState viewState = draw.mViewState;
   7929         boolean isPictureAfterFirstLayout = viewState != null;
   7930 
   7931         if (updateBaseLayer) {
   7932             setBaseLayer(draw.mBaseLayer,
   7933                     getSettings().getShowVisualIndicator(),
   7934                     isPictureAfterFirstLayout);
   7935         }
   7936         final Point viewSize = draw.mViewSize;
   7937         // We update the layout (i.e. request a layout from the
   7938         // view system) if the last view size that we sent to
   7939         // WebCore matches the view size of the picture we just
   7940         // received in the fixed dimension.
   7941         final boolean updateLayout = viewSize.x == mLastWidthSent
   7942                 && viewSize.y == mLastHeightSent;
   7943         // Don't send scroll event for picture coming from webkit,
   7944         // since the new picture may cause a scroll event to override
   7945         // the saved history scroll position.
   7946         mSendScrollEvent = false;
   7947         recordNewContentSize(draw.mContentSize.x,
   7948                 draw.mContentSize.y, updateLayout);
   7949         if (isPictureAfterFirstLayout) {
   7950             // Reset the last sent data here since dealing with new page.
   7951             mLastWidthSent = 0;
   7952             mZoomManager.onFirstLayout(draw);
   7953             int scrollX = viewState.mShouldStartScrolledRight
   7954                     ? getContentWidth() : viewState.mScrollX;
   7955             int scrollY = viewState.mScrollY;
   7956             contentScrollTo(scrollX, scrollY, false);
   7957             if (!mDrawHistory) {
   7958                 // As we are on a new page, hide the keyboard
   7959                 hideSoftKeyboard();
   7960             }
   7961         }
   7962         mSendScrollEvent = true;
   7963 
   7964         int functor = 0;
   7965         boolean forceInval = isPictureAfterFirstLayout;
   7966         ViewRootImpl viewRoot = mWebView.getViewRootImpl();
   7967         if (mWebView.isHardwareAccelerated()
   7968                 && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE
   7969                 && viewRoot != null) {
   7970             functor = nativeGetDrawGLFunction(mNativeClass);
   7971             if (functor != 0) {
   7972                 // force an invalidate if functor attach not successful
   7973                 forceInval |= !viewRoot.attachFunctor(functor);
   7974             }
   7975         }
   7976 
   7977         if (functor == 0
   7978                 || forceInval
   7979                 || mWebView.getLayerType() != View.LAYER_TYPE_NONE) {
   7980             // invalidate the screen so that the next repaint will show new content
   7981             // TODO: partial invalidate
   7982             mWebView.invalidate();
   7983         }
   7984 
   7985         // update the zoom information based on the new picture
   7986         if (mZoomManager.onNewPicture(draw))
   7987             invalidate();
   7988 
   7989         if (isPictureAfterFirstLayout) {
   7990             mViewManager.postReadyToDrawAll();
   7991         }
   7992         scrollEditWithCursor();
   7993 
   7994         if (mPictureListener != null) {
   7995             if (!mWebView.isHardwareAccelerated()
   7996                     || mWebView.getLayerType() == View.LAYER_TYPE_SOFTWARE) {
   7997                 // trigger picture listener for software layers. Hardware layers are
   7998                 // triggered in pageSwapCallback
   7999                 mPictureListener.onNewPicture(getWebView(), capturePicture());
   8000             }
   8001         }
   8002     }
   8003 
   8004     /**
   8005      * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
   8006      * and UPDATE_TEXT_SELECTION_MSG_ID.
   8007      */
   8008     private void updateTextSelectionFromMessage(int nodePointer,
   8009             int textGeneration, WebViewCore.TextSelectionData data) {
   8010         if (textGeneration == mTextGeneration) {
   8011             if (mInputConnection != null && mFieldPointer == nodePointer) {
   8012                 mInputConnection.setSelection(data.mStart, data.mEnd);
   8013             }
   8014         }
   8015         nativeSetTextSelection(mNativeClass, data.mSelectTextPtr);
   8016 
   8017         if ((data.mSelectionReason == TextSelectionData.REASON_ACCESSIBILITY_INJECTOR)
   8018                 || (!mSelectingText && data.mStart != data.mEnd
   8019                         && data.mSelectionReason != TextSelectionData.REASON_SELECT_WORD)) {
   8020             selectionDone();
   8021             mShowTextSelectionExtra = true;
   8022             invalidate();
   8023             return;
   8024         }
   8025 
   8026         if (data.mSelectTextPtr != 0 &&
   8027                 (data.mStart != data.mEnd ||
   8028                 (mFieldPointer == nodePointer && mFieldPointer != 0) ||
   8029                 (nodePointer == 0 && data.mStart == 0 && data.mEnd == 0))) {
   8030             mIsEditingText = (mFieldPointer == nodePointer) && nodePointer != 0;
   8031             mIsCaretSelection = (data.mStart == data.mEnd && nodePointer != 0);
   8032             if (mIsCaretSelection &&
   8033                     (mInputConnection == null ||
   8034                     mInputConnection.getEditable().length() == 0)) {
   8035                 // There's no text, don't show caret handle.
   8036                 selectionDone();
   8037             } else {
   8038                 if (!mSelectingText) {
   8039                     setupWebkitSelect();
   8040                 } else {
   8041                     syncSelectionCursors();
   8042                 }
   8043                 animateHandles();
   8044                 if (mIsCaretSelection) {
   8045                     resetCaretTimer();
   8046                 }
   8047             }
   8048         } else {
   8049             selectionDone();
   8050         }
   8051         invalidate();
   8052     }
   8053 
   8054     private void scrollEditText(int scrollX, int scrollY) {
   8055         // Scrollable edit text. Scroll it.
   8056         float maxScrollX = getMaxTextScrollX();
   8057         float scrollPercentX = ((float)scrollX)/maxScrollX;
   8058         mEditTextContent.offsetTo(-scrollX, -scrollY);
   8059         mWebViewCore.removeMessages(EventHub.SCROLL_TEXT_INPUT);
   8060         mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0,
   8061                 scrollY, (Float)scrollPercentX);
   8062         animateHandles();
   8063     }
   8064 
   8065     private void beginTextBatch() {
   8066         mIsBatchingTextChanges = true;
   8067     }
   8068 
   8069     private void commitTextBatch() {
   8070         if (mWebViewCore != null) {
   8071             mWebViewCore.sendMessages(mBatchedTextChanges);
   8072         }
   8073         mBatchedTextChanges.clear();
   8074         mIsBatchingTextChanges = false;
   8075     }
   8076 
   8077     void sendBatchableInputMessage(int what, int arg1, int arg2,
   8078             Object obj) {
   8079         if (mWebViewCore == null) {
   8080             return;
   8081         }
   8082         Message message = Message.obtain(null, what, arg1, arg2, obj);
   8083         if (mIsBatchingTextChanges) {
   8084             mBatchedTextChanges.add(message);
   8085         } else {
   8086             mWebViewCore.sendMessage(message);
   8087         }
   8088     }
   8089 
   8090     // Class used to use a dropdown for a <select> element
   8091     private class InvokeListBox implements Runnable {
   8092         // Whether the listbox allows multiple selection.
   8093         private boolean     mMultiple;
   8094         // Passed in to a list with multiple selection to tell
   8095         // which items are selected.
   8096         private int[]       mSelectedArray;
   8097         // Passed in to a list with single selection to tell
   8098         // where the initial selection is.
   8099         private int         mSelection;
   8100 
   8101         private Container[] mContainers;
   8102 
   8103         // Need these to provide stable ids to my ArrayAdapter,
   8104         // which normally does not have stable ids. (Bug 1250098)
   8105         private class Container extends Object {
   8106             /**
   8107              * Possible values for mEnabled.  Keep in sync with OptionStatus in
   8108              * WebViewCore.cpp
   8109              */
   8110             final static int OPTGROUP = -1;
   8111             final static int OPTION_DISABLED = 0;
   8112             final static int OPTION_ENABLED = 1;
   8113 
   8114             String  mString;
   8115             int     mEnabled;
   8116             int     mId;
   8117 
   8118             @Override
   8119             public String toString() {
   8120                 return mString;
   8121             }
   8122         }
   8123 
   8124         /**
   8125          *  Subclass ArrayAdapter so we can disable OptionGroupLabels,
   8126          *  and allow filtering.
   8127          */
   8128         private class MyArrayListAdapter extends ArrayAdapter<Container> {
   8129             public MyArrayListAdapter() {
   8130                 super(WebViewClassic.this.mContext,
   8131                         mMultiple ? com.android.internal.R.layout.select_dialog_multichoice :
   8132                         com.android.internal.R.layout.webview_select_singlechoice,
   8133                         mContainers);
   8134             }
   8135 
   8136             @Override
   8137             public View getView(int position, View convertView,
   8138                     ViewGroup parent) {
   8139                 // Always pass in null so that we will get a new CheckedTextView
   8140                 // Otherwise, an item which was previously used as an <optgroup>
   8141                 // element (i.e. has no check), could get used as an <option>
   8142                 // element, which needs a checkbox/radio, but it would not have
   8143                 // one.
   8144                 convertView = super.getView(position, null, parent);
   8145                 Container c = item(position);
   8146                 if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
   8147                     // ListView does not draw dividers between disabled and
   8148                     // enabled elements.  Use a LinearLayout to provide dividers
   8149                     LinearLayout layout = new LinearLayout(mContext);
   8150                     layout.setOrientation(LinearLayout.VERTICAL);
   8151                     if (position > 0) {
   8152                         View dividerTop = new View(mContext);
   8153                         dividerTop.setBackgroundResource(
   8154                                 android.R.drawable.divider_horizontal_bright);
   8155                         layout.addView(dividerTop);
   8156                     }
   8157 
   8158                     if (Container.OPTGROUP == c.mEnabled) {
   8159                         // Currently select_dialog_multichoice uses CheckedTextViews.
   8160                         // If that changes, the class cast will no longer be valid.
   8161                         if (mMultiple) {
   8162                             Assert.assertTrue(convertView instanceof CheckedTextView);
   8163                             ((CheckedTextView) convertView).setCheckMarkDrawable(null);
   8164                         }
   8165                     } else {
   8166                         // c.mEnabled == Container.OPTION_DISABLED
   8167                         // Draw the disabled element in a disabled state.
   8168                         convertView.setEnabled(false);
   8169                     }
   8170 
   8171                     layout.addView(convertView);
   8172                     if (position < getCount() - 1) {
   8173                         View dividerBottom = new View(mContext);
   8174                         dividerBottom.setBackgroundResource(
   8175                                 android.R.drawable.divider_horizontal_bright);
   8176                         layout.addView(dividerBottom);
   8177                     }
   8178                     return layout;
   8179                 }
   8180                 return convertView;
   8181             }
   8182 
   8183             @Override
   8184             public boolean hasStableIds() {
   8185                 // AdapterView's onChanged method uses this to determine whether
   8186                 // to restore the old state.  Return false so that the old (out
   8187                 // of date) state does not replace the new, valid state.
   8188                 return false;
   8189             }
   8190 
   8191             private Container item(int position) {
   8192                 if (position < 0 || position >= getCount()) {
   8193                     return null;
   8194                 }
   8195                 return getItem(position);
   8196             }
   8197 
   8198             @Override
   8199             public long getItemId(int position) {
   8200                 Container item = item(position);
   8201                 if (item == null) {
   8202                     return -1;
   8203                 }
   8204                 return item.mId;
   8205             }
   8206 
   8207             @Override
   8208             public boolean areAllItemsEnabled() {
   8209                 return false;
   8210             }
   8211 
   8212             @Override
   8213             public boolean isEnabled(int position) {
   8214                 Container item = item(position);
   8215                 if (item == null) {
   8216                     return false;
   8217                 }
   8218                 return Container.OPTION_ENABLED == item.mEnabled;
   8219             }
   8220         }
   8221 
   8222         private InvokeListBox(String[] array, int[] enabled, int[] selected) {
   8223             mMultiple = true;
   8224             mSelectedArray = selected;
   8225 
   8226             int length = array.length;
   8227             mContainers = new Container[length];
   8228             for (int i = 0; i < length; i++) {
   8229                 mContainers[i] = new Container();
   8230                 mContainers[i].mString = array[i];
   8231                 mContainers[i].mEnabled = enabled[i];
   8232                 mContainers[i].mId = i;
   8233             }
   8234         }
   8235 
   8236         private InvokeListBox(String[] array, int[] enabled, int selection) {
   8237             mSelection = selection;
   8238             mMultiple = false;
   8239 
   8240             int length = array.length;
   8241             mContainers = new Container[length];
   8242             for (int i = 0; i < length; i++) {
   8243                 mContainers[i] = new Container();
   8244                 mContainers[i].mString = array[i];
   8245                 mContainers[i].mEnabled = enabled[i];
   8246                 mContainers[i].mId = i;
   8247             }
   8248         }
   8249 
   8250         /*
   8251          * Whenever the data set changes due to filtering, this class ensures
   8252          * that the checked item remains checked.
   8253          */
   8254         private class SingleDataSetObserver extends DataSetObserver {
   8255             private long        mCheckedId;
   8256             private ListView    mListView;
   8257             private Adapter     mAdapter;
   8258 
   8259             /*
   8260              * Create a new observer.
   8261              * @param id The ID of the item to keep checked.
   8262              * @param l ListView for getting and clearing the checked states
   8263              * @param a Adapter for getting the IDs
   8264              */
   8265             public SingleDataSetObserver(long id, ListView l, Adapter a) {
   8266                 mCheckedId = id;
   8267                 mListView = l;
   8268                 mAdapter = a;
   8269             }
   8270 
   8271             @Override
   8272             public void onChanged() {
   8273                 // The filter may have changed which item is checked.  Find the
   8274                 // item that the ListView thinks is checked.
   8275                 int position = mListView.getCheckedItemPosition();
   8276                 long id = mAdapter.getItemId(position);
   8277                 if (mCheckedId != id) {
   8278                     // Clear the ListView's idea of the checked item, since
   8279                     // it is incorrect
   8280                     mListView.clearChoices();
   8281                     // Search for mCheckedId.  If it is in the filtered list,
   8282                     // mark it as checked
   8283                     int count = mAdapter.getCount();
   8284                     for (int i = 0; i < count; i++) {
   8285                         if (mAdapter.getItemId(i) == mCheckedId) {
   8286                             mListView.setItemChecked(i, true);
   8287                             break;
   8288                         }
   8289                     }
   8290                 }
   8291             }
   8292         }
   8293 
   8294         @Override
   8295         public void run() {
   8296             if (mWebViewCore == null
   8297                     || getWebView().getWindowToken() == null
   8298                     || getWebView().getViewRootImpl() == null) {
   8299                 // We've been detached and/or destroyed since this was posted
   8300                 return;
   8301             }
   8302             final ListView listView = (ListView) LayoutInflater.from(mContext)
   8303                     .inflate(com.android.internal.R.layout.select_dialog, null);
   8304             final MyArrayListAdapter adapter = new MyArrayListAdapter();
   8305             AlertDialog.Builder b = new AlertDialog.Builder(mContext)
   8306                     .setView(listView).setCancelable(true)
   8307                     .setInverseBackgroundForced(true);
   8308 
   8309             if (mMultiple) {
   8310                 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
   8311                     @Override
   8312                     public void onClick(DialogInterface dialog, int which) {
   8313                         mWebViewCore.sendMessage(
   8314                                 EventHub.LISTBOX_CHOICES,
   8315                                 adapter.getCount(), 0,
   8316                                 listView.getCheckedItemPositions());
   8317                     }});
   8318                 b.setNegativeButton(android.R.string.cancel,
   8319                         new DialogInterface.OnClickListener() {
   8320                     @Override
   8321                     public void onClick(DialogInterface dialog, int which) {
   8322                         mWebViewCore.sendMessage(
   8323                                 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
   8324                 }});
   8325             }
   8326             mListBoxDialog = b.create();
   8327             listView.setAdapter(adapter);
   8328             listView.setFocusableInTouchMode(true);
   8329             // There is a bug (1250103) where the checks in a ListView with
   8330             // multiple items selected are associated with the positions, not
   8331             // the ids, so the items do not properly retain their checks when
   8332             // filtered.  Do not allow filtering on multiple lists until
   8333             // that bug is fixed.
   8334 
   8335             listView.setTextFilterEnabled(!mMultiple);
   8336             if (mMultiple) {
   8337                 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
   8338                 int length = mSelectedArray.length;
   8339                 for (int i = 0; i < length; i++) {
   8340                     listView.setItemChecked(mSelectedArray[i], true);
   8341                 }
   8342             } else {
   8343                 listView.setOnItemClickListener(new OnItemClickListener() {
   8344                     @Override
   8345                     public void onItemClick(AdapterView<?> parent, View v,
   8346                             int position, long id) {
   8347                         // Rather than sending the message right away, send it
   8348                         // after the page regains focus.
   8349                         mListBoxMessage = Message.obtain(null,
   8350                                 EventHub.SINGLE_LISTBOX_CHOICE, (int) id, 0);
   8351                         if (mListBoxDialog != null) {
   8352                             mListBoxDialog.dismiss();
   8353                             mListBoxDialog = null;
   8354                         }
   8355                     }
   8356                 });
   8357                 if (mSelection != -1) {
   8358                     listView.setSelection(mSelection);
   8359                     listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
   8360                     listView.setItemChecked(mSelection, true);
   8361                     DataSetObserver observer = new SingleDataSetObserver(
   8362                             adapter.getItemId(mSelection), listView, adapter);
   8363                     adapter.registerDataSetObserver(observer);
   8364                 }
   8365             }
   8366             mListBoxDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
   8367                 @Override
   8368                 public void onCancel(DialogInterface dialog) {
   8369                     mWebViewCore.sendMessage(
   8370                                 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
   8371                     mListBoxDialog = null;
   8372                 }
   8373             });
   8374             mListBoxDialog.show();
   8375         }
   8376     }
   8377 
   8378     private Message mListBoxMessage;
   8379 
   8380     /*
   8381      * Request a dropdown menu for a listbox with multiple selection.
   8382      *
   8383      * @param array Labels for the listbox.
   8384      * @param enabledArray  State for each element in the list.  See static
   8385      *      integers in Container class.
   8386      * @param selectedArray Which positions are initally selected.
   8387      */
   8388     void requestListBox(String[] array, int[] enabledArray, int[]
   8389             selectedArray) {
   8390         mPrivateHandler.post(
   8391                 new InvokeListBox(array, enabledArray, selectedArray));
   8392     }
   8393 
   8394     /*
   8395      * Request a dropdown menu for a listbox with single selection or a single
   8396      * <select> element.
   8397      *
   8398      * @param array Labels for the listbox.
   8399      * @param enabledArray  State for each element in the list.  See static
   8400      *      integers in Container class.
   8401      * @param selection Which position is initally selected.
   8402      */
   8403     void requestListBox(String[] array, int[] enabledArray, int selection) {
   8404         mPrivateHandler.post(
   8405                 new InvokeListBox(array, enabledArray, selection));
   8406     }
   8407 
   8408     private int getScaledMaxXScroll() {
   8409         int width;
   8410         if (mHeightCanMeasure == false) {
   8411             width = getViewWidth() / 4;
   8412         } else {
   8413             Rect visRect = new Rect();
   8414             calcOurVisibleRect(visRect);
   8415             width = visRect.width() / 2;
   8416         }
   8417         // FIXME the divisor should be retrieved from somewhere
   8418         return viewToContentX(width);
   8419     }
   8420 
   8421     private int getScaledMaxYScroll() {
   8422         int height;
   8423         if (mHeightCanMeasure == false) {
   8424             height = getViewHeight() / 4;
   8425         } else {
   8426             Rect visRect = new Rect();
   8427             calcOurVisibleRect(visRect);
   8428             height = visRect.height() / 2;
   8429         }
   8430         // FIXME the divisor should be retrieved from somewhere
   8431         // the closest thing today is hard-coded into ScrollView.java
   8432         // (from ScrollView.java, line 363)   int maxJump = height/2;
   8433         return Math.round(height * mZoomManager.getInvScale());
   8434     }
   8435 
   8436     /**
   8437      * Called by JNI to invalidate view
   8438      */
   8439     private void viewInvalidate() {
   8440         invalidate();
   8441     }
   8442 
   8443     /**
   8444      * Pass the key directly to the page.  This assumes that
   8445      * nativePageShouldHandleShiftAndArrows() returned true.
   8446      */
   8447     private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) {
   8448         int keyEventAction;
   8449         if (down) {
   8450             keyEventAction = KeyEvent.ACTION_DOWN;
   8451         } else {
   8452             keyEventAction = KeyEvent.ACTION_UP;
   8453         }
   8454 
   8455         KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
   8456                 1, (metaState & KeyEvent.META_SHIFT_ON)
   8457                 | (metaState & KeyEvent.META_ALT_ON)
   8458                 | (metaState & KeyEvent.META_SYM_ON)
   8459                 , KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0);
   8460         sendKeyEvent(event);
   8461     }
   8462 
   8463     private void sendKeyEvent(KeyEvent event) {
   8464         int direction = 0;
   8465         switch (event.getKeyCode()) {
   8466         case KeyEvent.KEYCODE_DPAD_DOWN:
   8467             direction = View.FOCUS_DOWN;
   8468             break;
   8469         case KeyEvent.KEYCODE_DPAD_UP:
   8470             direction = View.FOCUS_UP;
   8471             break;
   8472         case KeyEvent.KEYCODE_DPAD_LEFT:
   8473             direction = View.FOCUS_LEFT;
   8474             break;
   8475         case KeyEvent.KEYCODE_DPAD_RIGHT:
   8476             direction = View.FOCUS_RIGHT;
   8477             break;
   8478         case KeyEvent.KEYCODE_TAB:
   8479             direction = event.isShiftPressed() ? View.FOCUS_BACKWARD : View.FOCUS_FORWARD;
   8480             break;
   8481         }
   8482         if (direction != 0 && mWebView.focusSearch(direction) == null) {
   8483             // Can't take focus in that direction
   8484             direction = 0;
   8485         }
   8486         int eventHubAction = EventHub.KEY_UP;
   8487         if (event.getAction() == KeyEvent.ACTION_DOWN) {
   8488             eventHubAction = EventHub.KEY_DOWN;
   8489             int sound = keyCodeToSoundsEffect(event.getKeyCode());
   8490             if (sound != 0) {
   8491                 mWebView.playSoundEffect(sound);
   8492             }
   8493         }
   8494         sendBatchableInputMessage(eventHubAction, direction, 0, event);
   8495     }
   8496 
   8497     /**
   8498      * See {@link WebView#setBackgroundColor(int)}
   8499      */
   8500     @Override
   8501     public void setBackgroundColor(int color) {
   8502         mBackgroundColor = color;
   8503         mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
   8504     }
   8505 
   8506     /**
   8507      * Enable the communication b/t the webView and VideoViewProxy
   8508      *
   8509      * only used by the Browser
   8510      */
   8511     public void setHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
   8512         mHTML5VideoViewProxy = proxy;
   8513     }
   8514 
   8515     /**
   8516      * Set the time to wait between passing touches to WebCore. See also the
   8517      * TOUCH_SENT_INTERVAL member for further discussion.
   8518      *
   8519      * This is only used by the DRT test application.
   8520      */
   8521     public void setTouchInterval(int interval) {
   8522         mCurrentTouchInterval = interval;
   8523     }
   8524 
   8525     /**
   8526      * Copy text into the clipboard. This is called indirectly from
   8527      * WebViewCore.
   8528      * @param text The text to put into the clipboard.
   8529      */
   8530     private void copyToClipboard(String text) {
   8531         ClipboardManager cm = (ClipboardManager)mContext
   8532                 .getSystemService(Context.CLIPBOARD_SERVICE);
   8533         ClipData clip = ClipData.newPlainText(getTitle(), text);
   8534         cm.setPrimaryClip(clip);
   8535     }
   8536 
   8537     /*package*/ void autoFillForm(int autoFillQueryId) {
   8538         mPrivateHandler.obtainMessage(AUTOFILL_FORM, autoFillQueryId, 0)
   8539             .sendToTarget();
   8540     }
   8541 
   8542     /* package */ ViewManager getViewManager() {
   8543         return mViewManager;
   8544     }
   8545 
   8546     /** send content invalidate */
   8547     protected void contentInvalidateAll() {
   8548         if (mWebViewCore != null && !mBlockWebkitViewMessages) {
   8549             mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL);
   8550         }
   8551     }
   8552 
   8553     /** discard all textures from tiles. Used in Profiled WebView */
   8554     public void discardAllTextures() {
   8555         nativeDiscardAllTextures();
   8556     }
   8557 
   8558     @Override
   8559     public void setLayerType(int layerType, Paint paint) {
   8560         updateHwAccelerated();
   8561     }
   8562 
   8563     private void updateHwAccelerated() {
   8564         if (mNativeClass == 0) {
   8565             return;
   8566         }
   8567         boolean hwAccelerated = false;
   8568         if (mWebView.isHardwareAccelerated()
   8569                 && mWebView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
   8570             hwAccelerated = true;
   8571         }
   8572 
   8573         // result is of type LayerAndroid::InvalidateFlags, non zero means invalidate/redraw
   8574         int result = nativeSetHwAccelerated(mNativeClass, hwAccelerated);
   8575         if (mWebViewCore != null && !mBlockWebkitViewMessages && result != 0) {
   8576             mWebViewCore.contentDraw();
   8577         }
   8578     }
   8579 
   8580     /**
   8581      * Begin collecting per-tile profiling data
   8582      *
   8583      * only used by profiling tests
   8584      */
   8585     public void tileProfilingStart() {
   8586         nativeTileProfilingStart();
   8587     }
   8588     /**
   8589      * Return per-tile profiling data
   8590      *
   8591      * only used by profiling tests
   8592      */
   8593     public float tileProfilingStop() {
   8594         return nativeTileProfilingStop();
   8595     }
   8596 
   8597     /** only used by profiling tests */
   8598     public void tileProfilingClear() {
   8599         nativeTileProfilingClear();
   8600     }
   8601     /** only used by profiling tests */
   8602     public int tileProfilingNumFrames() {
   8603         return nativeTileProfilingNumFrames();
   8604     }
   8605     /** only used by profiling tests */
   8606     public int tileProfilingNumTilesInFrame(int frame) {
   8607         return nativeTileProfilingNumTilesInFrame(frame);
   8608     }
   8609     /** only used by profiling tests */
   8610     public int tileProfilingGetInt(int frame, int tile, String key) {
   8611         return nativeTileProfilingGetInt(frame, tile, key);
   8612     }
   8613     /** only used by profiling tests */
   8614     public float tileProfilingGetFloat(int frame, int tile, String key) {
   8615         return nativeTileProfilingGetFloat(frame, tile, key);
   8616     }
   8617 
   8618     /**
   8619      * Checks the focused content for an editable text field. This can be
   8620      * text input or ContentEditable.
   8621      * @return true if the focused item is an editable text field.
   8622      */
   8623     boolean focusCandidateIsEditableText() {
   8624         if (mFocusedNode != null) {
   8625             return mFocusedNode.mEditable;
   8626         }
   8627         return false;
   8628     }
   8629 
   8630     // Called via JNI
   8631     private void postInvalidate() {
   8632         mWebView.postInvalidate();
   8633     }
   8634 
   8635     // Note: must be called before first WebViewClassic is created.
   8636     public static void setShouldMonitorWebCoreThread() {
   8637         WebViewCore.setShouldMonitorWebCoreThread();
   8638     }
   8639 
   8640     @Override
   8641     public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
   8642         int layer = getBaseLayer();
   8643         if (layer != 0) {
   8644             try {
   8645                 ByteArrayOutputStream stream = new ByteArrayOutputStream();
   8646                 ViewStateSerializer.dumpLayerHierarchy(layer, stream, level);
   8647                 stream.close();
   8648                 byte[] buf = stream.toByteArray();
   8649                 out.write(new String(buf, "ascii"));
   8650             } catch (IOException e) {}
   8651         }
   8652     }
   8653 
   8654     @Override
   8655     public View findHierarchyView(String className, int hashCode) {
   8656         if (mNativeClass == 0) return null;
   8657         Picture pic = new Picture();
   8658         if (!nativeDumpLayerContentToPicture(mNativeClass, className, hashCode, pic)) {
   8659             return null;
   8660         }
   8661         return new PictureWrapperView(getContext(), pic, mWebView);
   8662     }
   8663 
   8664     private static class PictureWrapperView extends View {
   8665         Picture mPicture;
   8666         WebView mWebView;
   8667 
   8668         public PictureWrapperView(Context context, Picture picture, WebView parent) {
   8669             super(context);
   8670             mPicture = picture;
   8671             mWebView = parent;
   8672             setWillNotDraw(false);
   8673             setRight(mPicture.getWidth());
   8674             setBottom(mPicture.getHeight());
   8675         }
   8676 
   8677         @Override
   8678         protected void onDraw(Canvas canvas) {
   8679             canvas.drawPicture(mPicture);
   8680         }
   8681 
   8682         @Override
   8683         public boolean post(Runnable action) {
   8684             return mWebView.post(action);
   8685         }
   8686     }
   8687 
   8688     private native void     nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx);
   8689     private native void     nativeDebugDump();
   8690     private static native void nativeDestroy(int ptr);
   8691 
   8692     private native void nativeDraw(Canvas canvas, RectF visibleRect,
   8693             int color, int extra);
   8694     private native void     nativeDumpDisplayTree(String urlOrNull);
   8695     private native boolean  nativeEvaluateLayersAnimations(int nativeInstance);
   8696     private native int      nativeCreateDrawGLFunction(int nativeInstance, Rect invScreenRect,
   8697             Rect screenRect, RectF visibleContentRect, float scale, int extras);
   8698     private native int      nativeGetDrawGLFunction(int nativeInstance);
   8699     private native void     nativeUpdateDrawGLFunction(int nativeInstance, Rect invScreenRect,
   8700             Rect screenRect, RectF visibleContentRect, float scale);
   8701     private native String   nativeGetSelection();
   8702     private native void     nativeSetHeightCanMeasure(boolean measure);
   8703     private native boolean  nativeSetBaseLayer(int nativeInstance,
   8704             int layer, boolean showVisualIndicator, boolean isPictureAfterFirstLayout,
   8705             int scrollingLayer);
   8706     private native int      nativeGetBaseLayer(int nativeInstance);
   8707     private native void     nativeCopyBaseContentToPicture(Picture pict);
   8708     private native boolean     nativeDumpLayerContentToPicture(int nativeInstance,
   8709             String className, int layerId, Picture pict);
   8710     private native boolean  nativeHasContent();
   8711     private native void     nativeStopGL(int ptr);
   8712     private native void     nativeDiscardAllTextures();
   8713     private native void     nativeTileProfilingStart();
   8714     private native float    nativeTileProfilingStop();
   8715     private native void     nativeTileProfilingClear();
   8716     private native int      nativeTileProfilingNumFrames();
   8717     private native int      nativeTileProfilingNumTilesInFrame(int frame);
   8718     private native int      nativeTileProfilingGetInt(int frame, int tile, String key);
   8719     private native float    nativeTileProfilingGetFloat(int frame, int tile, String key);
   8720 
   8721     private native void     nativeUseHardwareAccelSkia(boolean enabled);
   8722 
   8723     // Returns a pointer to the scrollable LayerAndroid at the given point.
   8724     private native int      nativeScrollableLayer(int nativeInstance, int x, int y, Rect scrollRect,
   8725             Rect scrollBounds);
   8726     /**
   8727      * Scroll the specified layer.
   8728      * @param nativeInstance Native WebView instance
   8729      * @param layer Id of the layer to scroll, as determined by nativeScrollableLayer.
   8730      * @param newX Destination x position to which to scroll.
   8731      * @param newY Destination y position to which to scroll.
   8732      * @return True if the layer is successfully scrolled.
   8733      */
   8734     private native boolean  nativeScrollLayer(int nativeInstance, int layer, int newX, int newY);
   8735     private native void     nativeSetIsScrolling(boolean isScrolling);
   8736     private native int      nativeGetBackgroundColor(int nativeInstance);
   8737     native boolean  nativeSetProperty(String key, String value);
   8738     native String   nativeGetProperty(String key);
   8739     /**
   8740      * See {@link ComponentCallbacks2} for the trim levels and descriptions
   8741      */
   8742     private static native void     nativeOnTrimMemory(int level);
   8743     private static native void nativeSetPauseDrawing(int instance, boolean pause);
   8744     private static native void nativeSetTextSelection(int instance, int selection);
   8745     private static native int nativeGetHandleLayerId(int instance, int handle,
   8746             Point cursorLocation, QuadF textQuad);
   8747     private static native void nativeMapLayerRect(int instance, int layerId,
   8748             Rect rect);
   8749     // Returns 1 if a layer sync is needed, else 0
   8750     private static native int nativeSetHwAccelerated(int instance, boolean hwAccelerated);
   8751     private static native void nativeFindMaxVisibleRect(int instance, int layerId,
   8752             Rect visibleContentRect);
   8753     private static native boolean nativeIsHandleLeft(int instance, int handleId);
   8754     private static native boolean nativeIsPointVisible(int instance,
   8755             int layerId, int contentX, int contentY);
   8756 }
   8757