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