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