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