Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2007 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.content.Context;
     20 import android.content.Intent;
     21 import android.content.pm.PackageManager.NameNotFoundException;
     22 import android.database.Cursor;
     23 import android.graphics.Canvas;
     24 import android.graphics.DrawFilter;
     25 import android.graphics.Paint;
     26 import android.graphics.PaintFlagsDrawFilter;
     27 import android.graphics.Picture;
     28 import android.graphics.Point;
     29 import android.graphics.Rect;
     30 import android.graphics.Region;
     31 import android.net.Uri;
     32 import android.os.Handler;
     33 import android.os.Looper;
     34 import android.os.Message;
     35 import android.os.Process;
     36 import android.provider.Browser;
     37 import android.provider.OpenableColumns;
     38 import android.util.Log;
     39 import android.util.SparseBooleanArray;
     40 import android.view.KeyEvent;
     41 import android.view.SurfaceHolder;
     42 import android.view.SurfaceView;
     43 import android.view.View;
     44 
     45 import java.util.ArrayList;
     46 import java.util.Collection;
     47 import java.util.Map;
     48 import java.util.Set;
     49 
     50 import junit.framework.Assert;
     51 
     52 final class WebViewCore {
     53 
     54     private static final String LOGTAG = "webcore";
     55 
     56     static {
     57         // Load libwebcore during static initialization. This happens in the
     58         // zygote process so it will be shared read-only across all app
     59         // processes.
     60         System.loadLibrary("webcore");
     61     }
     62 
     63     /*
     64      * WebViewCore always executes in the same thread as the native webkit.
     65      */
     66 
     67     // The WebView that corresponds to this WebViewCore.
     68     private WebView mWebView;
     69     // Proxy for handling callbacks from native code
     70     private final CallbackProxy mCallbackProxy;
     71     // Settings object for maintaining all settings
     72     private final WebSettings mSettings;
     73     // Context for initializing the BrowserFrame with the proper assets.
     74     private final Context mContext;
     75     // The pointer to a native view object.
     76     private int mNativeClass;
     77     // The BrowserFrame is an interface to the native Frame component.
     78     private BrowserFrame mBrowserFrame;
     79     // Custom JS interfaces to add during the initialization.
     80     private Map<String, Object> mJavascriptInterfaces;
     81     /*
     82      * range is from 200 to 10,000. 0 is a special value means device-width. -1
     83      * means undefined.
     84      */
     85     private int mViewportWidth = -1;
     86 
     87     /*
     88      * range is from 200 to 10,000. 0 is a special value means device-height. -1
     89      * means undefined.
     90      */
     91     private int mViewportHeight = -1;
     92 
     93     /*
     94      * scale in percent, range is from 1 to 1000. 0 means undefined.
     95      */
     96     private int mViewportInitialScale = 0;
     97 
     98     /*
     99      * scale in percent, range is from 1 to 1000. 0 means undefined.
    100      */
    101     private int mViewportMinimumScale = 0;
    102 
    103     /*
    104      * scale in percent, range is from 1 to 1000. 0 means undefined.
    105      */
    106     private int mViewportMaximumScale = 0;
    107 
    108     private boolean mViewportUserScalable = true;
    109 
    110     /*
    111      * range is from 70 to 400.
    112      * 0 is a special value means device-dpi. The default scale factor will be
    113      * always 100.
    114      * -1 means undefined. The default scale factor will be
    115      * WebView.DEFAULT_SCALE_PERCENT.
    116      */
    117     private int mViewportDensityDpi = -1;
    118 
    119     private int mRestoredScale = 0;
    120     private int mRestoredScreenWidthScale = 0;
    121     private int mRestoredX = 0;
    122     private int mRestoredY = 0;
    123 
    124     private int mWebkitScrollX = 0;
    125     private int mWebkitScrollY = 0;
    126 
    127     // The thread name used to identify the WebCore thread and for use in
    128     // debugging other classes that require operation within the WebCore thread.
    129     /* package */ static final String THREAD_NAME = "WebViewCoreThread";
    130 
    131     public WebViewCore(Context context, WebView w, CallbackProxy proxy,
    132             Map<String, Object> javascriptInterfaces) {
    133         // No need to assign this in the WebCore thread.
    134         mCallbackProxy = proxy;
    135         mWebView = w;
    136         mJavascriptInterfaces = javascriptInterfaces;
    137         // This context object is used to initialize the WebViewCore during
    138         // subwindow creation.
    139         mContext = context;
    140 
    141         // We need to wait for the initial thread creation before sending
    142         // a message to the WebCore thread.
    143         // XXX: This is the only time the UI thread will wait for the WebCore
    144         // thread!
    145         synchronized (WebViewCore.class) {
    146             if (sWebCoreHandler == null) {
    147                 // Create a global thread and start it.
    148                 Thread t = new Thread(new WebCoreThread());
    149                 t.setName(THREAD_NAME);
    150                 t.start();
    151                 try {
    152                     WebViewCore.class.wait();
    153                 } catch (InterruptedException e) {
    154                     Log.e(LOGTAG, "Caught exception while waiting for thread " +
    155                            "creation.");
    156                     Log.e(LOGTAG, Log.getStackTraceString(e));
    157                 }
    158             }
    159         }
    160         // Create an EventHub to handle messages before and after the thread is
    161         // ready.
    162         mEventHub = new EventHub();
    163         // Create a WebSettings object for maintaining all settings
    164         mSettings = new WebSettings(mContext, mWebView);
    165         // The WebIconDatabase needs to be initialized within the UI thread so
    166         // just request the instance here.
    167         WebIconDatabase.getInstance();
    168         // Create the WebStorage singleton and the UI handler
    169         WebStorage.getInstance().createUIHandler();
    170         // Create the UI handler for GeolocationPermissions
    171         GeolocationPermissions.getInstance().createUIHandler();
    172         // Send a message to initialize the WebViewCore.
    173         Message init = sWebCoreHandler.obtainMessage(
    174                 WebCoreThread.INITIALIZE, this);
    175         sWebCoreHandler.sendMessage(init);
    176     }
    177 
    178     /* Initialize private data within the WebCore thread.
    179      */
    180     private void initialize() {
    181         /* Initialize our private BrowserFrame class to handle all
    182          * frame-related functions. We need to create a new view which
    183          * in turn creates a C level FrameView and attaches it to the frame.
    184          */
    185         mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy,
    186                 mSettings, mJavascriptInterfaces);
    187         mJavascriptInterfaces = null;
    188         // Sync the native settings and also create the WebCore thread handler.
    189         mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
    190         // Create the handler and transfer messages for the IconDatabase
    191         WebIconDatabase.getInstance().createHandler();
    192         // Create the handler for WebStorage
    193         WebStorage.getInstance().createHandler();
    194         // Create the handler for GeolocationPermissions.
    195         GeolocationPermissions.getInstance().createHandler();
    196         // The transferMessages call will transfer all pending messages to the
    197         // WebCore thread handler.
    198         mEventHub.transferMessages();
    199 
    200         // Send a message back to WebView to tell it that we have set up the
    201         // WebCore thread.
    202         if (mWebView != null) {
    203             Message.obtain(mWebView.mPrivateHandler,
    204                     WebView.WEBCORE_INITIALIZED_MSG_ID,
    205                     mNativeClass, 0).sendToTarget();
    206         }
    207 
    208     }
    209 
    210     /* Handle the initialization of WebViewCore during subwindow creation. This
    211      * method is called from the WebCore thread but it is called before the
    212      * INITIALIZE message can be handled.
    213      */
    214     /* package */ void initializeSubwindow() {
    215         // Go ahead and initialize the core components.
    216         initialize();
    217         // Remove the INITIALIZE method so we don't try to initialize twice.
    218         sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this);
    219     }
    220 
    221     /* Get the BrowserFrame component. This is used for subwindow creation and
    222      * is called only from BrowserFrame in the WebCore thread. */
    223     /* package */ BrowserFrame getBrowserFrame() {
    224         return mBrowserFrame;
    225     }
    226 
    227     //-------------------------------------------------------------------------
    228     // Common methods
    229     //-------------------------------------------------------------------------
    230 
    231     /**
    232      * Causes all timers to pause. This applies to all WebViews in the current
    233      * app process.
    234      */
    235     public static void pauseTimers() {
    236         if (BrowserFrame.sJavaBridge == null) {
    237             throw new IllegalStateException(
    238                     "No WebView has been created in this process!");
    239         }
    240         BrowserFrame.sJavaBridge.pause();
    241     }
    242 
    243     /**
    244      * Resume all timers. This applies to all WebViews in the current process.
    245      */
    246     public static void resumeTimers() {
    247         if (BrowserFrame.sJavaBridge == null) {
    248             throw new IllegalStateException(
    249                     "No WebView has been created in this process!");
    250         }
    251         BrowserFrame.sJavaBridge.resume();
    252     }
    253 
    254     public WebSettings getSettings() {
    255         return mSettings;
    256     }
    257 
    258     /**
    259      * Add an error message to the client's console.
    260      * @param message The message to add
    261      * @param lineNumber the line on which the error occurred
    262      * @param sourceID the filename of the source that caused the error.
    263      * @param msgLevel the log level of this message. This is a value casted to int
    264      *     from WebCore::MessageLevel in WebCore/page/Console.h.
    265      */
    266     protected void addMessageToConsole(String message, int lineNumber, String sourceID,
    267             int msgLevel) {
    268         mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID, msgLevel);
    269     }
    270 
    271     /**
    272      * Invoke a javascript alert.
    273      * @param message The message displayed in the alert.
    274      */
    275     protected void jsAlert(String url, String message) {
    276         mCallbackProxy.onJsAlert(url, message);
    277     }
    278 
    279 
    280     /**
    281      * Called by JNI.  Open a file chooser to upload a file.
    282      * @return String version of the URI plus the name of the file.
    283      * FIXME: Just return the URI here, and in FileSystem::pathGetFileName, call
    284      * into Java to get the filename.
    285      */
    286     private String openFileChooser() {
    287         Uri uri = mCallbackProxy.openFileChooser();
    288         if (uri == null) return "";
    289         // Find out the name, and append it to the URI.
    290         // Webkit will treat the name as the filename, and
    291         // the URI as the path.  The URI will be used
    292         // in BrowserFrame to get the actual data.
    293         Cursor cursor = mContext.getContentResolver().query(
    294                 uri,
    295                 new String[] { OpenableColumns.DISPLAY_NAME },
    296                 null,
    297                 null,
    298                 null);
    299         String name = "";
    300         if (cursor != null) {
    301             try {
    302                 if (cursor.moveToNext()) {
    303                     name = cursor.getString(0);
    304                 }
    305             } finally {
    306                 cursor.close();
    307             }
    308         }
    309         return uri.toString() + "/" + name;
    310     }
    311 
    312     /**
    313      * Notify the browser that the origin has exceeded it's database quota.
    314      * @param url The URL that caused the overflow.
    315      * @param databaseIdentifier The identifier of the database.
    316      * @param currentQuota The current quota for the origin.
    317      * @param estimatedSize The estimated size of the database.
    318      */
    319     protected void exceededDatabaseQuota(String url,
    320                                          String databaseIdentifier,
    321                                          long currentQuota,
    322                                          long estimatedSize) {
    323         // Inform the callback proxy of the quota overflow. Send an object
    324         // that encapsulates a call to the nativeSetDatabaseQuota method to
    325         // awaken the sleeping webcore thread when a decision from the
    326         // client to allow or deny quota is available.
    327         mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
    328                 currentQuota, estimatedSize, getUsedQuota(),
    329                 new WebStorage.QuotaUpdater() {
    330                         public void updateQuota(long quota) {
    331                             nativeSetNewStorageLimit(quota);
    332                         }
    333                 });
    334     }
    335 
    336     /**
    337      * Notify the browser that the appcache has exceeded its max size.
    338      * @param spaceNeeded is the amount of disk space that would be needed
    339      * in order for the last appcache operation to succeed.
    340      */
    341     protected void reachedMaxAppCacheSize(long spaceNeeded) {
    342         mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(),
    343                 new WebStorage.QuotaUpdater() {
    344                     public void updateQuota(long quota) {
    345                         nativeSetNewStorageLimit(quota);
    346                     }
    347                 });
    348     }
    349 
    350     protected void populateVisitedLinks() {
    351         ValueCallback callback = new ValueCallback<String[]>() {
    352             public void onReceiveValue(String[] value) {
    353                 sendMessage(EventHub.POPULATE_VISITED_LINKS, (Object)value);
    354             }
    355         };
    356         mCallbackProxy.getVisitedHistory(callback);
    357     }
    358 
    359     /**
    360      * Shows a prompt to ask the user to set the Geolocation permission state
    361      * for the given origin.
    362      * @param origin The origin for which Geolocation permissions are
    363      *     requested.
    364      */
    365     protected void geolocationPermissionsShowPrompt(String origin) {
    366         mCallbackProxy.onGeolocationPermissionsShowPrompt(origin,
    367                 new GeolocationPermissions.Callback() {
    368           public void invoke(String origin, boolean allow, boolean remember) {
    369             GeolocationPermissionsData data = new GeolocationPermissionsData();
    370             data.mOrigin = origin;
    371             data.mAllow = allow;
    372             data.mRemember = remember;
    373             // Marshall to WebCore thread.
    374             sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data);
    375           }
    376         });
    377     }
    378 
    379     /**
    380      * Hides the Geolocation permissions prompt.
    381      */
    382     protected void geolocationPermissionsHidePrompt() {
    383         mCallbackProxy.onGeolocationPermissionsHidePrompt();
    384     }
    385 
    386     /**
    387      * Invoke a javascript confirm dialog.
    388      * @param message The message displayed in the dialog.
    389      * @return True if the user confirmed or false if the user cancelled.
    390      */
    391     protected boolean jsConfirm(String url, String message) {
    392         return mCallbackProxy.onJsConfirm(url, message);
    393     }
    394 
    395     /**
    396      * Invoke a javascript prompt dialog.
    397      * @param message The message to be displayed in the dialog.
    398      * @param defaultValue The default value in the prompt input.
    399      * @return The input from the user or null to indicate the user cancelled
    400      *         the dialog.
    401      */
    402     protected String jsPrompt(String url, String message, String defaultValue) {
    403         return mCallbackProxy.onJsPrompt(url, message, defaultValue);
    404     }
    405 
    406     /**
    407      * Invoke a javascript before unload dialog.
    408      * @param url The url that is requesting the dialog.
    409      * @param message The message displayed in the dialog.
    410      * @return True if the user confirmed or false if the user cancelled. False
    411      *         will cancel the navigation.
    412      */
    413     protected boolean jsUnload(String url, String message) {
    414         return mCallbackProxy.onJsBeforeUnload(url, message);
    415     }
    416 
    417     /**
    418      *
    419      * Callback to notify that a JavaScript execution timeout has occured.
    420      * @return True if the JavaScript execution should be interrupted. False
    421      *         will continue the execution.
    422      */
    423     protected boolean jsInterrupt() {
    424         return mCallbackProxy.onJsTimeout();
    425     }
    426 
    427     //-------------------------------------------------------------------------
    428     // JNI methods
    429     //-------------------------------------------------------------------------
    430 
    431     static native String nativeFindAddress(String addr, boolean caseInsensitive);
    432 
    433     /**
    434      * Empty the picture set.
    435      */
    436     private native void nativeClearContent();
    437 
    438     /**
    439      * Create a flat picture from the set of pictures.
    440      */
    441     private native void nativeCopyContentToPicture(Picture picture);
    442 
    443     /**
    444      * Draw the picture set with a background color. Returns true
    445      * if some individual picture took too long to draw and can be
    446      * split into parts. Called from the UI thread.
    447      */
    448     private native boolean nativeDrawContent(Canvas canvas, int color);
    449 
    450     /**
    451      * check to see if picture is blank and in progress
    452      */
    453     private native boolean nativePictureReady();
    454 
    455     /**
    456      * Redraw a portion of the picture set. The Point wh returns the
    457      * width and height of the overall picture.
    458      */
    459     private native boolean nativeRecordContent(Region invalRegion, Point wh);
    460 
    461     private native boolean nativeFocusBoundsChanged();
    462 
    463     /**
    464      * Splits slow parts of the picture set. Called from the webkit
    465      * thread after nativeDrawContent returns true.
    466      */
    467     private native void nativeSplitContent();
    468 
    469     private native boolean nativeKey(int keyCode, int unichar,
    470             int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
    471             boolean isDown);
    472 
    473     private native void nativeClick(int framePtr, int nodePtr);
    474 
    475     private native void nativeSendListBoxChoices(boolean[] choices, int size);
    476 
    477     private native void nativeSendListBoxChoice(int choice);
    478 
    479     /*  Tell webkit what its width and height are, for the purposes
    480         of layout/line-breaking. These coordinates are in document space,
    481         which is the same as View coords unless we have zoomed the document
    482         (see nativeSetZoom).
    483         screenWidth is used by layout to wrap column around. If viewport uses
    484         fixed size, screenWidth can be different from width with zooming.
    485         should this be called nativeSetViewPortSize?
    486     */
    487     private native void nativeSetSize(int width, int height, int screenWidth,
    488             float scale, int realScreenWidth, int screenHeight, int anchorX,
    489             int anchorY, boolean ignoreHeight);
    490 
    491     private native int nativeGetContentMinPrefWidth();
    492 
    493     // Start: functions that deal with text editing
    494     private native void nativeReplaceTextfieldText(
    495             int oldStart, int oldEnd, String replace, int newStart, int newEnd,
    496             int textGeneration);
    497 
    498     private native void passToJs(int gen,
    499             String currentText, int keyCode, int keyValue, boolean down,
    500             boolean cap, boolean fn, boolean sym);
    501 
    502     private native void nativeSetFocusControllerActive(boolean active);
    503 
    504     private native void nativeSaveDocumentState(int frame);
    505 
    506     private native void nativeMoveFocus(int framePtr, int nodePointer);
    507     private native void nativeMoveMouse(int framePtr, int x, int y);
    508 
    509     private native void nativeMoveMouseIfLatest(int moveGeneration,
    510             int framePtr, int x, int y);
    511 
    512     private native String nativeRetrieveHref(int framePtr, int nodePtr);
    513     private native String nativeRetrieveAnchorText(int framePtr, int nodePtr);
    514 
    515     private native void nativeTouchUp(int touchGeneration,
    516             int framePtr, int nodePtr, int x, int y);
    517 
    518     private native boolean nativeHandleTouchEvent(int action, int x, int y,
    519             int metaState);
    520 
    521     private native void nativeUpdateFrameCache();
    522 
    523     private native void nativeSetBackgroundColor(int color);
    524 
    525     private native void nativeDumpDomTree(boolean useFile);
    526 
    527     private native void nativeDumpRenderTree(boolean useFile);
    528 
    529     private native void nativeDumpNavTree();
    530 
    531     private native void nativeDumpV8Counters();
    532 
    533     private native void nativeSetJsFlags(String flags);
    534 
    535     /**
    536      *  Delete text from start to end in the focused textfield. If there is no
    537      *  focus, or if start == end, silently fail.  If start and end are out of
    538      *  order, swap them.
    539      *  @param  start   Beginning of selection to delete.
    540      *  @param  end     End of selection to delete.
    541      *  @param  textGeneration Text generation number when delete was pressed.
    542      */
    543     private native void nativeDeleteSelection(int start, int end,
    544             int textGeneration);
    545 
    546     /**
    547      *  Set the selection to (start, end) in the focused textfield. If start and
    548      *  end are out of order, swap them.
    549      *  @param  start   Beginning of selection.
    550      *  @param  end     End of selection.
    551      */
    552     private native void nativeSetSelection(int start, int end);
    553 
    554     // Register a scheme to be treated as local scheme so that it can access
    555     // local asset files for resources
    556     private native void nativeRegisterURLSchemeAsLocal(String scheme);
    557 
    558     /*
    559      * Inform webcore that the user has decided whether to allow or deny new
    560      * quota for the current origin or more space for the app cache, and that
    561      * the main thread should wake up now.
    562      * @param limit Is the new quota for an origin or new app cache max size.
    563      */
    564     private native void nativeSetNewStorageLimit(long limit);
    565 
    566     /**
    567      * Provide WebCore with a Geolocation permission state for the specified
    568      * origin.
    569      * @param origin The origin for which Geolocation permissions are provided.
    570      * @param allow Whether Geolocation permissions are allowed.
    571      * @param remember Whether this decision should be remembered beyond the
    572      *     life of the current page.
    573      */
    574     private native void nativeGeolocationPermissionsProvide(String origin, boolean allow, boolean remember);
    575 
    576     /**
    577      * Provide WebCore with the previously visted links from the history database
    578      */
    579     private native void  nativeProvideVisitedHistory(String[] history);
    580 
    581     // EventHub for processing messages
    582     private final EventHub mEventHub;
    583     // WebCore thread handler
    584     private static Handler sWebCoreHandler;
    585     // Class for providing Handler creation inside the WebCore thread.
    586     private static class WebCoreThread implements Runnable {
    587         // Message id for initializing a new WebViewCore.
    588         private static final int INITIALIZE = 0;
    589         private static final int REDUCE_PRIORITY = 1;
    590         private static final int RESUME_PRIORITY = 2;
    591 
    592         public void run() {
    593             Looper.prepare();
    594             Assert.assertNull(sWebCoreHandler);
    595             synchronized (WebViewCore.class) {
    596                 sWebCoreHandler = new Handler() {
    597                     @Override
    598                     public void handleMessage(Message msg) {
    599                         switch (msg.what) {
    600                             case INITIALIZE:
    601                                 WebViewCore core = (WebViewCore) msg.obj;
    602                                 core.initialize();
    603                                 break;
    604 
    605                             case REDUCE_PRIORITY:
    606                                 // 3 is an adjustable number.
    607                                 Process.setThreadPriority(
    608                                         Process.THREAD_PRIORITY_DEFAULT + 3 *
    609                                         Process.THREAD_PRIORITY_LESS_FAVORABLE);
    610                                 break;
    611 
    612                             case RESUME_PRIORITY:
    613                                 Process.setThreadPriority(
    614                                         Process.THREAD_PRIORITY_DEFAULT);
    615                                 break;
    616                         }
    617                     }
    618                 };
    619                 WebViewCore.class.notify();
    620             }
    621             Looper.loop();
    622         }
    623     }
    624 
    625     static class BaseUrlData {
    626         String mBaseUrl;
    627         String mData;
    628         String mMimeType;
    629         String mEncoding;
    630         String mHistoryUrl;
    631     }
    632 
    633     static class CursorData {
    634         CursorData() {}
    635         CursorData(int frame, int node, int x, int y) {
    636             mFrame = frame;
    637             mNode = node;
    638             mX = x;
    639             mY = y;
    640         }
    641         int mMoveGeneration;
    642         int mFrame;
    643         int mNode;
    644         int mX;
    645         int mY;
    646     }
    647 
    648     static class JSInterfaceData {
    649         Object mObject;
    650         String mInterfaceName;
    651     }
    652 
    653     static class JSKeyData {
    654         String mCurrentText;
    655         KeyEvent mEvent;
    656     }
    657 
    658     static class MotionUpData {
    659         int mFrame;
    660         int mNode;
    661         Rect mBounds;
    662         int mX;
    663         int mY;
    664     }
    665 
    666     static class GetUrlData {
    667         String mUrl;
    668         Map<String, String> mExtraHeaders;
    669     }
    670 
    671     static class PostUrlData {
    672         String mUrl;
    673         byte[] mPostData;
    674     }
    675 
    676     static class ReplaceTextData {
    677         String mReplace;
    678         int mNewStart;
    679         int mNewEnd;
    680         int mTextGeneration;
    681     }
    682 
    683     static class TextSelectionData {
    684         public TextSelectionData(int start, int end) {
    685             mStart = start;
    686             mEnd = end;
    687         }
    688         int mStart;
    689         int mEnd;
    690     }
    691 
    692     static class TouchUpData {
    693         int mMoveGeneration;
    694         int mFrame;
    695         int mNode;
    696         int mX;
    697         int mY;
    698     }
    699 
    700     // mAction of TouchEventData can be MotionEvent.getAction() which uses the
    701     // last two bytes or one of the following values
    702     static final int ACTION_LONGPRESS = 0x100;
    703     static final int ACTION_DOUBLETAP = 0x200;
    704 
    705     static class TouchEventData {
    706         int mAction;
    707         int mX;
    708         int mY;
    709         int mMetaState;
    710         boolean mReprocess;
    711         float mViewX;
    712         float mViewY;
    713     }
    714 
    715     static class GeolocationPermissionsData {
    716         String mOrigin;
    717         boolean mAllow;
    718         boolean mRemember;
    719     }
    720 
    721 
    722 
    723         static final String[] HandlerDebugString = {
    724             "REQUEST_LABEL", // 97
    725             "UPDATE_FRAME_CACHE_IF_LOADING", // = 98
    726             "SCROLL_TEXT_INPUT", // = 99
    727             "LOAD_URL", // = 100;
    728             "STOP_LOADING", // = 101;
    729             "RELOAD", // = 102;
    730             "KEY_DOWN", // = 103;
    731             "KEY_UP", // = 104;
    732             "VIEW_SIZE_CHANGED", // = 105;
    733             "GO_BACK_FORWARD", // = 106;
    734             "SET_SCROLL_OFFSET", // = 107;
    735             "RESTORE_STATE", // = 108;
    736             "PAUSE_TIMERS", // = 109;
    737             "RESUME_TIMERS", // = 110;
    738             "CLEAR_CACHE", // = 111;
    739             "CLEAR_HISTORY", // = 112;
    740             "SET_SELECTION", // = 113;
    741             "REPLACE_TEXT", // = 114;
    742             "PASS_TO_JS", // = 115;
    743             "SET_GLOBAL_BOUNDS", // = 116;
    744             "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117;
    745             "CLICK", // = 118;
    746             "SET_NETWORK_STATE", // = 119;
    747             "DOC_HAS_IMAGES", // = 120;
    748             "121", // = 121;
    749             "DELETE_SELECTION", // = 122;
    750             "LISTBOX_CHOICES", // = 123;
    751             "SINGLE_LISTBOX_CHOICE", // = 124;
    752             "MESSAGE_RELAY", // = 125;
    753             "SET_BACKGROUND_COLOR", // = 126;
    754             "SET_MOVE_FOCUS", // = 127
    755             "SAVE_DOCUMENT_STATE", // = 128;
    756             "129", // = 129;
    757             "WEBKIT_DRAW", // = 130;
    758             "SYNC_SCROLL", // = 131;
    759             "POST_URL", // = 132;
    760             "SPLIT_PICTURE_SET", // = 133;
    761             "CLEAR_CONTENT", // = 134;
    762             "SET_MOVE_MOUSE", // = 135;
    763             "SET_MOVE_MOUSE_IF_LATEST", // = 136;
    764             "REQUEST_CURSOR_HREF", // = 137;
    765             "ADD_JS_INTERFACE", // = 138;
    766             "LOAD_DATA", // = 139;
    767             "TOUCH_UP", // = 140;
    768             "TOUCH_EVENT", // = 141;
    769             "SET_ACTIVE", // = 142;
    770             "ON_PAUSE",     // = 143
    771             "ON_RESUME",    // = 144
    772             "FREE_MEMORY",  // = 145
    773             "VALID_NODE_BOUNDS", // = 146
    774         };
    775 
    776     class EventHub {
    777         // Message Ids
    778         static final int REQUEST_LABEL = 97;
    779         static final int UPDATE_FRAME_CACHE_IF_LOADING = 98;
    780         static final int SCROLL_TEXT_INPUT = 99;
    781         static final int LOAD_URL = 100;
    782         static final int STOP_LOADING = 101;
    783         static final int RELOAD = 102;
    784         static final int KEY_DOWN = 103;
    785         static final int KEY_UP = 104;
    786         static final int VIEW_SIZE_CHANGED = 105;
    787         static final int GO_BACK_FORWARD = 106;
    788         static final int SET_SCROLL_OFFSET = 107;
    789         static final int RESTORE_STATE = 108;
    790         static final int PAUSE_TIMERS = 109;
    791         static final int RESUME_TIMERS = 110;
    792         static final int CLEAR_CACHE = 111;
    793         static final int CLEAR_HISTORY = 112;
    794         static final int SET_SELECTION = 113;
    795         static final int REPLACE_TEXT = 114;
    796         static final int PASS_TO_JS = 115;
    797         static final int SET_GLOBAL_BOUNDS = 116;
    798         static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117;
    799         static final int CLICK = 118;
    800         static final int SET_NETWORK_STATE = 119;
    801         static final int DOC_HAS_IMAGES = 120;
    802         static final int DELETE_SELECTION = 122;
    803         static final int LISTBOX_CHOICES = 123;
    804         static final int SINGLE_LISTBOX_CHOICE = 124;
    805         static final int MESSAGE_RELAY = 125;
    806         static final int SET_BACKGROUND_COLOR = 126;
    807         static final int SET_MOVE_FOCUS = 127;
    808         static final int SAVE_DOCUMENT_STATE = 128;
    809 
    810         static final int WEBKIT_DRAW = 130;
    811         static final int SYNC_SCROLL = 131;
    812         static final int POST_URL = 132;
    813         static final int SPLIT_PICTURE_SET = 133;
    814         static final int CLEAR_CONTENT = 134;
    815 
    816         // UI nav messages
    817         static final int SET_MOVE_MOUSE = 135;
    818         static final int SET_MOVE_MOUSE_IF_LATEST = 136;
    819         static final int REQUEST_CURSOR_HREF = 137;
    820         static final int ADD_JS_INTERFACE = 138;
    821         static final int LOAD_DATA = 139;
    822 
    823         // motion
    824         static final int TOUCH_UP = 140;
    825         // message used to pass UI touch events to WebCore
    826         static final int TOUCH_EVENT = 141;
    827 
    828         // Used to tell the focus controller not to draw the blinking cursor,
    829         // based on whether the WebView has focus and whether the WebView's
    830         // cursor matches the webpage's focus.
    831         static final int SET_ACTIVE = 142;
    832 
    833         // lifecycle activities for just this DOM (unlike pauseTimers, which
    834         // is global)
    835         static final int ON_PAUSE = 143;
    836         static final int ON_RESUME = 144;
    837         static final int FREE_MEMORY = 145;
    838         static final int VALID_NODE_BOUNDS = 146;
    839 
    840         // Network-based messaging
    841         static final int CLEAR_SSL_PREF_TABLE = 150;
    842 
    843         // Test harness messages
    844         static final int REQUEST_EXT_REPRESENTATION = 160;
    845         static final int REQUEST_DOC_AS_TEXT = 161;
    846 
    847         // debugging
    848         static final int DUMP_DOMTREE = 170;
    849         static final int DUMP_RENDERTREE = 171;
    850         static final int DUMP_NAVTREE = 172;
    851         static final int DUMP_V8COUNTERS = 173;
    852 
    853         static final int SET_JS_FLAGS = 174;
    854         // Geolocation
    855         static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180;
    856 
    857         static final int POPULATE_VISITED_LINKS = 181;
    858 
    859         static final int HIDE_FULLSCREEN = 182;
    860 
    861         static final int SET_NETWORK_TYPE = 183;
    862 
    863         // navigator.isApplicationInstalled()
    864         static final int ADD_PACKAGE_NAMES = 184;
    865         static final int ADD_PACKAGE_NAME = 185;
    866         static final int REMOVE_PACKAGE_NAME = 186;
    867 
    868         // private message ids
    869         private static final int DESTROY =     200;
    870 
    871         // Private handler for WebCore messages.
    872         private Handler mHandler;
    873         // Message queue for containing messages before the WebCore thread is
    874         // ready.
    875         private ArrayList<Message> mMessages = new ArrayList<Message>();
    876         // Flag for blocking messages. This is used during DESTROY to avoid
    877         // posting more messages to the EventHub or to WebView's event handler.
    878         private boolean mBlockMessages;
    879 
    880         private int mTid;
    881         private int mSavedPriority;
    882 
    883         /**
    884          * Prevent other classes from creating an EventHub.
    885          */
    886         private EventHub() {}
    887 
    888         /**
    889          * Transfer all messages to the newly created webcore thread handler.
    890          */
    891         private void transferMessages() {
    892             mTid = Process.myTid();
    893             mSavedPriority = Process.getThreadPriority(mTid);
    894 
    895             mHandler = new Handler() {
    896                 @Override
    897                 public void handleMessage(Message msg) {
    898                     if (DebugFlags.WEB_VIEW_CORE) {
    899                         Log.v(LOGTAG, (msg.what < REQUEST_LABEL
    900                                 || msg.what
    901                                 > VALID_NODE_BOUNDS ? Integer.toString(msg.what)
    902                                 : HandlerDebugString[msg.what
    903                                         - REQUEST_LABEL])
    904                                 + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
    905                                 + " obj=" + msg.obj);
    906                     }
    907                     switch (msg.what) {
    908                         case WEBKIT_DRAW:
    909                             webkitDraw();
    910                             break;
    911 
    912                         case DESTROY:
    913                             // Time to take down the world. Cancel all pending
    914                             // loads and destroy the native view and frame.
    915                             synchronized (WebViewCore.this) {
    916                                 mBrowserFrame.destroy();
    917                                 mBrowserFrame = null;
    918                                 mSettings.onDestroyed();
    919                                 mNativeClass = 0;
    920                                 mWebView = null;
    921                             }
    922                             break;
    923 
    924                         case REQUEST_LABEL:
    925                             if (mWebView != null) {
    926                                 int nodePointer = msg.arg2;
    927                                 String label = nativeRequestLabel(msg.arg1,
    928                                         nodePointer);
    929                                 if (label != null && label.length() > 0) {
    930                                     Message.obtain(mWebView.mPrivateHandler,
    931                                             WebView.RETURN_LABEL, nodePointer,
    932                                             0, label).sendToTarget();
    933                                 }
    934                             }
    935                             break;
    936 
    937                         case UPDATE_FRAME_CACHE_IF_LOADING:
    938                             nativeUpdateFrameCacheIfLoading();
    939                             break;
    940 
    941                         case SCROLL_TEXT_INPUT:
    942                             nativeScrollFocusedTextInput(
    943                                     ((Float) msg.obj).floatValue(), msg.arg1);
    944                             break;
    945 
    946                         case LOAD_URL: {
    947                             GetUrlData param = (GetUrlData) msg.obj;
    948                             loadUrl(param.mUrl, param.mExtraHeaders);
    949                             break;
    950                         }
    951 
    952                         case POST_URL: {
    953                             PostUrlData param = (PostUrlData) msg.obj;
    954                             mBrowserFrame.postUrl(param.mUrl, param.mPostData);
    955                             break;
    956                         }
    957                         case LOAD_DATA:
    958                             BaseUrlData loadParams = (BaseUrlData) msg.obj;
    959                             String baseUrl = loadParams.mBaseUrl;
    960                             if (baseUrl != null) {
    961                                 int i = baseUrl.indexOf(':');
    962                                 if (i > 0) {
    963                                     /*
    964                                      * In 1.0, {@link
    965                                      * WebView#loadDataWithBaseURL} can access
    966                                      * local asset files as long as the data is
    967                                      * valid. In the new WebKit, the restriction
    968                                      * is tightened. To be compatible with 1.0,
    969                                      * we automatically add the scheme of the
    970                                      * baseUrl for local access as long as it is
    971                                      * not http(s)/ftp(s)/about/javascript
    972                                      */
    973                                     String scheme = baseUrl.substring(0, i);
    974                                     if (!scheme.startsWith("http") &&
    975                                             !scheme.startsWith("ftp") &&
    976                                             !scheme.startsWith("about") &&
    977                                             !scheme.startsWith("javascript")) {
    978                                         nativeRegisterURLSchemeAsLocal(scheme);
    979                                     }
    980                                 }
    981                             }
    982                             mBrowserFrame.loadData(baseUrl,
    983                                     loadParams.mData,
    984                                     loadParams.mMimeType,
    985                                     loadParams.mEncoding,
    986                                     loadParams.mHistoryUrl);
    987                             break;
    988 
    989                         case STOP_LOADING:
    990                             // If the WebCore has committed the load, but not
    991                             // finished the first layout yet, we need to set
    992                             // first layout done to trigger the interpreted side sync
    993                             // up with native side
    994                             if (mBrowserFrame.committed()
    995                                     && !mBrowserFrame.firstLayoutDone()) {
    996                                 mBrowserFrame.didFirstLayout();
    997                             }
    998                             // Do this after syncing up the layout state.
    999                             stopLoading();
   1000                             break;
   1001 
   1002                         case RELOAD:
   1003                             mBrowserFrame.reload(false);
   1004                             break;
   1005 
   1006                         case KEY_DOWN:
   1007                             key((KeyEvent) msg.obj, true);
   1008                             break;
   1009 
   1010                         case KEY_UP:
   1011                             key((KeyEvent) msg.obj, false);
   1012                             break;
   1013 
   1014                         case CLICK:
   1015                             nativeClick(msg.arg1, msg.arg2);
   1016                             break;
   1017 
   1018                         case VIEW_SIZE_CHANGED: {
   1019                             WebView.ViewSizeData data =
   1020                                     (WebView.ViewSizeData) msg.obj;
   1021                             viewSizeChanged(data.mWidth, data.mHeight,
   1022                                     data.mTextWrapWidth, data.mScale,
   1023                                     data.mAnchorX, data.mAnchorY,
   1024                                     data.mIgnoreHeight);
   1025                             break;
   1026                         }
   1027                         case SET_SCROLL_OFFSET:
   1028                             // note: these are in document coordinates
   1029                             // (inv-zoom)
   1030                             Point pt = (Point) msg.obj;
   1031                             nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
   1032                             break;
   1033 
   1034                         case SET_GLOBAL_BOUNDS:
   1035                             Rect r = (Rect) msg.obj;
   1036                             nativeSetGlobalBounds(r.left, r.top, r.width(),
   1037                                 r.height());
   1038                             break;
   1039 
   1040                         case GO_BACK_FORWARD:
   1041                             // If it is a standard load and the load is not
   1042                             // committed yet, we interpret BACK as RELOAD
   1043                             if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
   1044                                     (mBrowserFrame.loadType() ==
   1045                                     BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
   1046                                 mBrowserFrame.reload(true);
   1047                             } else {
   1048                                 mBrowserFrame.goBackOrForward(msg.arg1);
   1049                             }
   1050                             break;
   1051 
   1052                         case RESTORE_STATE:
   1053                             stopLoading();
   1054                             restoreState(msg.arg1);
   1055                             break;
   1056 
   1057                         case PAUSE_TIMERS:
   1058                             mSavedPriority = Process.getThreadPriority(mTid);
   1059                             Process.setThreadPriority(mTid,
   1060                                     Process.THREAD_PRIORITY_BACKGROUND);
   1061                             pauseTimers();
   1062                             WebViewWorker.getHandler().sendEmptyMessage(
   1063                                     WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION);
   1064                             break;
   1065 
   1066                         case RESUME_TIMERS:
   1067                             Process.setThreadPriority(mTid, mSavedPriority);
   1068                             resumeTimers();
   1069                             WebViewWorker.getHandler().sendEmptyMessage(
   1070                                     WebViewWorker.MSG_RESUME_CACHE_TRANSACTION);
   1071                             break;
   1072 
   1073                         case ON_PAUSE:
   1074                             nativePause();
   1075                             break;
   1076 
   1077                         case ON_RESUME:
   1078                             nativeResume();
   1079                             break;
   1080 
   1081                         case FREE_MEMORY:
   1082                             clearCache(false);
   1083                             nativeFreeMemory();
   1084                             break;
   1085 
   1086                         case SET_NETWORK_STATE:
   1087                             if (BrowserFrame.sJavaBridge == null) {
   1088                                 throw new IllegalStateException("No WebView " +
   1089                                         "has been created in this process!");
   1090                             }
   1091                             BrowserFrame.sJavaBridge
   1092                                     .setNetworkOnLine(msg.arg1 == 1);
   1093                             break;
   1094 
   1095                         case SET_NETWORK_TYPE:
   1096                             if (BrowserFrame.sJavaBridge == null) {
   1097                                 throw new IllegalStateException("No WebView " +
   1098                                         "has been created in this process!");
   1099                             }
   1100                             Map<String, String> map = (Map<String, String>) msg.obj;
   1101                             BrowserFrame.sJavaBridge
   1102                                     .setNetworkType(map.get("type"), map.get("subtype"));
   1103                             break;
   1104 
   1105                         case CLEAR_CACHE:
   1106                             clearCache(msg.arg1 == 1);
   1107                             break;
   1108 
   1109                         case CLEAR_HISTORY:
   1110                             mCallbackProxy.getBackForwardList().
   1111                                     close(mBrowserFrame.mNativeFrame);
   1112                             break;
   1113 
   1114                         case REPLACE_TEXT:
   1115                             ReplaceTextData rep = (ReplaceTextData) msg.obj;
   1116                             nativeReplaceTextfieldText(msg.arg1, msg.arg2,
   1117                                     rep.mReplace, rep.mNewStart, rep.mNewEnd,
   1118                                     rep.mTextGeneration);
   1119                             break;
   1120 
   1121                         case PASS_TO_JS: {
   1122                             JSKeyData jsData = (JSKeyData) msg.obj;
   1123                             KeyEvent evt = jsData.mEvent;
   1124                             int keyCode = evt.getKeyCode();
   1125                             int keyValue = evt.getUnicodeChar();
   1126                             int generation = msg.arg1;
   1127                             passToJs(generation,
   1128                                     jsData.mCurrentText,
   1129                                     keyCode,
   1130                                     keyValue,
   1131                                     evt.isDown(),
   1132                                     evt.isShiftPressed(), evt.isAltPressed(),
   1133                                     evt.isSymPressed());
   1134                             break;
   1135                         }
   1136 
   1137                         case SAVE_DOCUMENT_STATE: {
   1138                             CursorData cDat = (CursorData) msg.obj;
   1139                             nativeSaveDocumentState(cDat.mFrame);
   1140                             break;
   1141                         }
   1142 
   1143                         case CLEAR_SSL_PREF_TABLE:
   1144                             Network.getInstance(mContext)
   1145                                     .clearUserSslPrefTable();
   1146                             break;
   1147 
   1148                         case TOUCH_UP:
   1149                             TouchUpData touchUpData = (TouchUpData) msg.obj;
   1150                             nativeTouchUp(touchUpData.mMoveGeneration,
   1151                                     touchUpData.mFrame, touchUpData.mNode,
   1152                                     touchUpData.mX, touchUpData.mY);
   1153                             break;
   1154 
   1155                         case TOUCH_EVENT: {
   1156                             TouchEventData ted = (TouchEventData) msg.obj;
   1157                             Message.obtain(
   1158                                     mWebView.mPrivateHandler,
   1159                                     WebView.PREVENT_TOUCH_ID,
   1160                                     ted.mAction,
   1161                                     nativeHandleTouchEvent(ted.mAction, ted.mX,
   1162                                             ted.mY, ted.mMetaState) ? 1 : 0,
   1163                                     ted.mReprocess ? ted : null).sendToTarget();
   1164                             break;
   1165                         }
   1166 
   1167                         case SET_ACTIVE:
   1168                             nativeSetFocusControllerActive(msg.arg1 == 1);
   1169                             break;
   1170 
   1171                         case ADD_JS_INTERFACE:
   1172                             JSInterfaceData jsData = (JSInterfaceData) msg.obj;
   1173                             mBrowserFrame.addJavascriptInterface(jsData.mObject,
   1174                                     jsData.mInterfaceName);
   1175                             break;
   1176 
   1177                         case REQUEST_EXT_REPRESENTATION:
   1178                             mBrowserFrame.externalRepresentation(
   1179                                     (Message) msg.obj);
   1180                             break;
   1181 
   1182                         case REQUEST_DOC_AS_TEXT:
   1183                             mBrowserFrame.documentAsText((Message) msg.obj);
   1184                             break;
   1185 
   1186                         case SET_MOVE_FOCUS:
   1187                             CursorData focusData = (CursorData) msg.obj;
   1188                             nativeMoveFocus(focusData.mFrame, focusData.mNode);
   1189                             break;
   1190 
   1191                         case SET_MOVE_MOUSE:
   1192                             CursorData cursorData = (CursorData) msg.obj;
   1193                             nativeMoveMouse(cursorData.mFrame,
   1194                                      cursorData.mX, cursorData.mY);
   1195                             break;
   1196 
   1197                         case SET_MOVE_MOUSE_IF_LATEST:
   1198                             CursorData cData = (CursorData) msg.obj;
   1199                             nativeMoveMouseIfLatest(cData.mMoveGeneration,
   1200                                     cData.mFrame,
   1201                                     cData.mX, cData.mY);
   1202                             break;
   1203 
   1204                         case REQUEST_CURSOR_HREF: {
   1205                             Message hrefMsg = (Message) msg.obj;
   1206                             hrefMsg.getData().putString("url",
   1207                                     nativeRetrieveHref(msg.arg1, msg.arg2));
   1208                             hrefMsg.getData().putString("title",
   1209                                     nativeRetrieveAnchorText(msg.arg1, msg.arg2));
   1210                             hrefMsg.sendToTarget();
   1211                             break;
   1212                         }
   1213 
   1214                         case UPDATE_CACHE_AND_TEXT_ENTRY:
   1215                             nativeUpdateFrameCache();
   1216                             // FIXME: this should provide a minimal rectangle
   1217                             if (mWebView != null) {
   1218                                 mWebView.postInvalidate();
   1219                             }
   1220                             sendUpdateTextEntry();
   1221                             break;
   1222 
   1223                         case DOC_HAS_IMAGES:
   1224                             Message imageResult = (Message) msg.obj;
   1225                             imageResult.arg1 =
   1226                                     mBrowserFrame.documentHasImages() ? 1 : 0;
   1227                             imageResult.sendToTarget();
   1228                             break;
   1229 
   1230                         case DELETE_SELECTION:
   1231                             TextSelectionData deleteSelectionData
   1232                                     = (TextSelectionData) msg.obj;
   1233                             nativeDeleteSelection(deleteSelectionData.mStart,
   1234                                     deleteSelectionData.mEnd, msg.arg1);
   1235                             break;
   1236 
   1237                         case SET_SELECTION:
   1238                             nativeSetSelection(msg.arg1, msg.arg2);
   1239                             break;
   1240 
   1241                         case LISTBOX_CHOICES:
   1242                             SparseBooleanArray choices = (SparseBooleanArray)
   1243                                     msg.obj;
   1244                             int choicesSize = msg.arg1;
   1245                             boolean[] choicesArray = new boolean[choicesSize];
   1246                             for (int c = 0; c < choicesSize; c++) {
   1247                                 choicesArray[c] = choices.get(c);
   1248                             }
   1249                             nativeSendListBoxChoices(choicesArray,
   1250                                     choicesSize);
   1251                             break;
   1252 
   1253                         case SINGLE_LISTBOX_CHOICE:
   1254                             nativeSendListBoxChoice(msg.arg1);
   1255                             break;
   1256 
   1257                         case SET_BACKGROUND_COLOR:
   1258                             nativeSetBackgroundColor(msg.arg1);
   1259                             break;
   1260 
   1261                         case DUMP_DOMTREE:
   1262                             nativeDumpDomTree(msg.arg1 == 1);
   1263                             break;
   1264 
   1265                         case DUMP_RENDERTREE:
   1266                             nativeDumpRenderTree(msg.arg1 == 1);
   1267                             break;
   1268 
   1269                         case DUMP_NAVTREE:
   1270                             nativeDumpNavTree();
   1271                             break;
   1272 
   1273                         case DUMP_V8COUNTERS:
   1274                             nativeDumpV8Counters();
   1275                             break;
   1276 
   1277                         case SET_JS_FLAGS:
   1278                             nativeSetJsFlags((String)msg.obj);
   1279                             break;
   1280 
   1281                         case GEOLOCATION_PERMISSIONS_PROVIDE:
   1282                             GeolocationPermissionsData data =
   1283                                     (GeolocationPermissionsData) msg.obj;
   1284                             nativeGeolocationPermissionsProvide(data.mOrigin,
   1285                                     data.mAllow, data.mRemember);
   1286                             break;
   1287 
   1288                         case SYNC_SCROLL:
   1289                             mWebkitScrollX = msg.arg1;
   1290                             mWebkitScrollY = msg.arg2;
   1291                             break;
   1292 
   1293                         case SPLIT_PICTURE_SET:
   1294                             nativeSplitContent();
   1295                             mSplitPictureIsScheduled = false;
   1296                             break;
   1297 
   1298                         case CLEAR_CONTENT:
   1299                             // Clear the view so that onDraw() will draw nothing
   1300                             // but white background
   1301                             // (See public method WebView.clearView)
   1302                             nativeClearContent();
   1303                             break;
   1304 
   1305                         case MESSAGE_RELAY:
   1306                             if (msg.obj instanceof Message) {
   1307                                 ((Message) msg.obj).sendToTarget();
   1308                             }
   1309                             break;
   1310 
   1311                         case POPULATE_VISITED_LINKS:
   1312                             nativeProvideVisitedHistory((String[])msg.obj);
   1313                             break;
   1314 
   1315                         case VALID_NODE_BOUNDS: {
   1316                             MotionUpData motionUpData = (MotionUpData) msg.obj;
   1317                             if (!nativeValidNodeAndBounds(
   1318                                     motionUpData.mFrame, motionUpData.mNode,
   1319                                     motionUpData.mBounds)) {
   1320                                 nativeUpdateFrameCache();
   1321                             }
   1322                             Message message = mWebView.mPrivateHandler
   1323                                     .obtainMessage(WebView.DO_MOTION_UP,
   1324                                     motionUpData.mX, motionUpData.mY);
   1325                             mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(
   1326                                     message);
   1327                             break;
   1328                         }
   1329 
   1330                         case HIDE_FULLSCREEN:
   1331                             nativeFullScreenPluginHidden(msg.arg1);
   1332                             break;
   1333 
   1334                         case ADD_PACKAGE_NAMES:
   1335                             if (BrowserFrame.sJavaBridge == null) {
   1336                                 throw new IllegalStateException("No WebView " +
   1337                                         "has been created in this process!");
   1338                             }
   1339                             BrowserFrame.sJavaBridge.addPackageNames(
   1340                                     (Set<String>) msg.obj);
   1341                             break;
   1342 
   1343                         case ADD_PACKAGE_NAME:
   1344                             if (BrowserFrame.sJavaBridge == null) {
   1345                                 throw new IllegalStateException("No WebView " +
   1346                                         "has been created in this process!");
   1347                             }
   1348                             BrowserFrame.sJavaBridge.addPackageName(
   1349                                     (String) msg.obj);
   1350                             break;
   1351 
   1352                         case REMOVE_PACKAGE_NAME:
   1353                             if (BrowserFrame.sJavaBridge == null) {
   1354                                 throw new IllegalStateException("No WebView " +
   1355                                         "has been created in this process!");
   1356                             }
   1357                             BrowserFrame.sJavaBridge.removePackageName(
   1358                                     (String) msg.obj);
   1359                             break;
   1360                     }
   1361                 }
   1362             };
   1363             // Take all queued messages and resend them to the new handler.
   1364             synchronized (this) {
   1365                 int size = mMessages.size();
   1366                 for (int i = 0; i < size; i++) {
   1367                     mHandler.sendMessage(mMessages.get(i));
   1368                 }
   1369                 mMessages = null;
   1370             }
   1371         }
   1372 
   1373         /**
   1374          * Send a message internally to the queue or to the handler
   1375          */
   1376         private synchronized void sendMessage(Message msg) {
   1377             if (mBlockMessages) {
   1378                 return;
   1379             }
   1380             if (mMessages != null) {
   1381                 mMessages.add(msg);
   1382             } else {
   1383                 mHandler.sendMessage(msg);
   1384             }
   1385         }
   1386 
   1387         private synchronized void removeMessages(int what) {
   1388             if (mBlockMessages) {
   1389                 return;
   1390             }
   1391             if (what == EventHub.WEBKIT_DRAW) {
   1392                 mDrawIsScheduled = false;
   1393             }
   1394             if (mMessages != null) {
   1395                 Log.w(LOGTAG, "Not supported in this case.");
   1396             } else {
   1397                 mHandler.removeMessages(what);
   1398             }
   1399         }
   1400 
   1401         private synchronized boolean hasMessages(int what) {
   1402             if (mBlockMessages) {
   1403                 return false;
   1404             }
   1405             if (mMessages != null) {
   1406                 Log.w(LOGTAG, "hasMessages() is not supported in this case.");
   1407                 return false;
   1408             } else {
   1409                 return mHandler.hasMessages(what);
   1410             }
   1411         }
   1412 
   1413         private synchronized void sendMessageDelayed(Message msg, long delay) {
   1414             if (mBlockMessages) {
   1415                 return;
   1416             }
   1417             mHandler.sendMessageDelayed(msg, delay);
   1418         }
   1419 
   1420         /**
   1421          * Send a message internally to the front of the queue.
   1422          */
   1423         private synchronized void sendMessageAtFrontOfQueue(Message msg) {
   1424             if (mBlockMessages) {
   1425                 return;
   1426             }
   1427             if (mMessages != null) {
   1428                 mMessages.add(0, msg);
   1429             } else {
   1430                 mHandler.sendMessageAtFrontOfQueue(msg);
   1431             }
   1432         }
   1433 
   1434         /**
   1435          * Remove all the messages.
   1436          */
   1437         private synchronized void removeMessages() {
   1438             // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
   1439             mDrawIsScheduled = false;
   1440             mSplitPictureIsScheduled = false;
   1441             if (mMessages != null) {
   1442                 mMessages.clear();
   1443             } else {
   1444                 mHandler.removeCallbacksAndMessages(null);
   1445             }
   1446         }
   1447 
   1448         /**
   1449          * Block sending messages to the EventHub.
   1450          */
   1451         private synchronized void blockMessages() {
   1452             mBlockMessages = true;
   1453         }
   1454     }
   1455 
   1456     //-------------------------------------------------------------------------
   1457     // Methods called by host activity (in the same thread)
   1458     //-------------------------------------------------------------------------
   1459 
   1460     void stopLoading() {
   1461         if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
   1462         if (mBrowserFrame != null) {
   1463             mBrowserFrame.stopLoading();
   1464         }
   1465     }
   1466 
   1467     //-------------------------------------------------------------------------
   1468     // Methods called by WebView
   1469     // If it refers to local variable, it needs synchronized().
   1470     // If it needs WebCore, it has to send message.
   1471     //-------------------------------------------------------------------------
   1472 
   1473     void sendMessage(Message msg) {
   1474         mEventHub.sendMessage(msg);
   1475     }
   1476 
   1477     void sendMessage(int what) {
   1478         mEventHub.sendMessage(Message.obtain(null, what));
   1479     }
   1480 
   1481     void sendMessage(int what, Object obj) {
   1482         mEventHub.sendMessage(Message.obtain(null, what, obj));
   1483     }
   1484 
   1485     void sendMessage(int what, int arg1) {
   1486         // just ignore the second argument (make it 0)
   1487         mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
   1488     }
   1489 
   1490     void sendMessage(int what, int arg1, int arg2) {
   1491         mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
   1492     }
   1493 
   1494     void sendMessage(int what, int arg1, Object obj) {
   1495         // just ignore the second argument (make it 0)
   1496         mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
   1497     }
   1498 
   1499     void sendMessage(int what, int arg1, int arg2, Object obj) {
   1500         mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
   1501     }
   1502 
   1503     void sendMessageAtFrontOfQueue(int what, Object obj) {
   1504         mEventHub.sendMessageAtFrontOfQueue(Message.obtain(
   1505                 null, what, obj));
   1506     }
   1507 
   1508     void sendMessageDelayed(int what, Object obj, long delay) {
   1509         mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
   1510     }
   1511 
   1512     void removeMessages(int what) {
   1513         mEventHub.removeMessages(what);
   1514     }
   1515 
   1516     void removeMessages() {
   1517         mEventHub.removeMessages();
   1518     }
   1519 
   1520     /**
   1521      * Removes pending messages and trigger a DESTROY message to send to
   1522      * WebCore.
   1523      * Called from UI thread.
   1524      */
   1525     void destroy() {
   1526         // We don't want anyone to post a message between removing pending
   1527         // messages and sending the destroy message.
   1528         synchronized (mEventHub) {
   1529             // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
   1530             // be preserved even the WebView is destroyed.
   1531             // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
   1532             boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
   1533             boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
   1534             mEventHub.removeMessages();
   1535             mEventHub.sendMessageAtFrontOfQueue(
   1536                     Message.obtain(null, EventHub.DESTROY));
   1537             if (hasPause) {
   1538                 mEventHub.sendMessageAtFrontOfQueue(
   1539                         Message.obtain(null, EventHub.PAUSE_TIMERS));
   1540             }
   1541             if (hasResume) {
   1542                 mEventHub.sendMessageAtFrontOfQueue(
   1543                         Message.obtain(null, EventHub.RESUME_TIMERS));
   1544             }
   1545             mEventHub.blockMessages();
   1546         }
   1547     }
   1548 
   1549     //-------------------------------------------------------------------------
   1550     // WebViewCore private methods
   1551     //-------------------------------------------------------------------------
   1552 
   1553     private void clearCache(boolean includeDiskFiles) {
   1554         mBrowserFrame.clearCache();
   1555         if (includeDiskFiles) {
   1556             CacheManager.removeAllCacheFiles();
   1557         }
   1558     }
   1559 
   1560     private void loadUrl(String url, Map<String, String> extraHeaders) {
   1561         if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
   1562         mBrowserFrame.loadUrl(url, extraHeaders);
   1563     }
   1564 
   1565     private void key(KeyEvent evt, boolean isDown) {
   1566         if (DebugFlags.WEB_VIEW_CORE) {
   1567             Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
   1568                     + evt);
   1569         }
   1570         int keyCode = evt.getKeyCode();
   1571         if (!nativeKey(keyCode, evt.getUnicodeChar(),
   1572                 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
   1573                 evt.isSymPressed(),
   1574                 isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
   1575             if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
   1576                     && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
   1577                 if (DebugFlags.WEB_VIEW_CORE) {
   1578                     Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
   1579                 }
   1580                 if (mWebView != null && evt.isDown()) {
   1581                     Message.obtain(mWebView.mPrivateHandler,
   1582                             WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
   1583                 }
   1584                 return;
   1585             }
   1586             // bubble up the event handling
   1587             // but do not bubble up the ENTER key, which would open the search
   1588             // bar without any text.
   1589             mCallbackProxy.onUnhandledKeyEvent(evt);
   1590         }
   1591     }
   1592 
   1593     // These values are used to avoid requesting a layout based on old values
   1594     private int mCurrentViewWidth = 0;
   1595     private int mCurrentViewHeight = 0;
   1596     private float mCurrentViewScale = 1.0f;
   1597 
   1598     // notify webkit that our virtual view size changed size (after inv-zoom)
   1599     private void viewSizeChanged(int w, int h, int textwrapWidth, float scale,
   1600             int anchorX, int anchorY, boolean ignoreHeight) {
   1601         if (DebugFlags.WEB_VIEW_CORE) {
   1602             Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
   1603                     + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
   1604         }
   1605         if (w == 0) {
   1606             Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
   1607             return;
   1608         }
   1609         int width = w;
   1610         if (mSettings.getUseWideViewPort()) {
   1611             if (mViewportWidth == -1) {
   1612                 if (mSettings.getLayoutAlgorithm() ==
   1613                         WebSettings.LayoutAlgorithm.NORMAL) {
   1614                     width = WebView.DEFAULT_VIEWPORT_WIDTH;
   1615                 } else {
   1616                     /*
   1617                      * if a page's minimum preferred width is wider than the
   1618                      * given "w", use it instead to get better layout result. If
   1619                      * we start a page with MAX_ZOOM_WIDTH, "w" will be always
   1620                      * wider. If we start a page with screen width, due to the
   1621                      * delay between {@link #didFirstLayout} and
   1622                      * {@link #viewSizeChanged},
   1623                      * {@link #nativeGetContentMinPrefWidth} will return a more
   1624                      * accurate value than initial 0 to result a better layout.
   1625                      * In the worse case, the native width will be adjusted when
   1626                      * next zoom or screen orientation change happens.
   1627                      */
   1628                     width = Math.min(WebView.sMaxViewportWidth, Math.max(w,
   1629                             Math.max(WebView.DEFAULT_VIEWPORT_WIDTH,
   1630                                     nativeGetContentMinPrefWidth())));
   1631                 }
   1632             } else if (mViewportWidth > 0) {
   1633                 width = Math.max(w, mViewportWidth);
   1634             } else {
   1635                 width = textwrapWidth;
   1636             }
   1637         }
   1638         nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
   1639                 textwrapWidth, scale, w, h, anchorX, anchorY, ignoreHeight);
   1640         // Remember the current width and height
   1641         boolean needInvalidate = (mCurrentViewWidth == 0);
   1642         mCurrentViewWidth = w;
   1643         mCurrentViewHeight = h;
   1644         mCurrentViewScale = scale;
   1645         if (needInvalidate) {
   1646             // ensure {@link #webkitDraw} is called as we were blocking in
   1647             // {@link #contentDraw} when mCurrentViewWidth is 0
   1648             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
   1649             contentDraw();
   1650         }
   1651         mEventHub.sendMessage(Message.obtain(null,
   1652                 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
   1653     }
   1654 
   1655     private void sendUpdateTextEntry() {
   1656         if (mWebView != null) {
   1657             Message.obtain(mWebView.mPrivateHandler,
   1658                     WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
   1659         }
   1660     }
   1661 
   1662     // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
   1663     // callbacks. Computes the sum of database quota for all origins.
   1664     private long getUsedQuota() {
   1665         WebStorage webStorage = WebStorage.getInstance();
   1666         Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
   1667 
   1668         if (origins == null) {
   1669             return 0;
   1670         }
   1671         long usedQuota = 0;
   1672         for (WebStorage.Origin website : origins) {
   1673             usedQuota += website.getQuota();
   1674         }
   1675         return usedQuota;
   1676     }
   1677 
   1678     // Used to avoid posting more than one draw message.
   1679     private boolean mDrawIsScheduled;
   1680 
   1681     // Used to avoid posting more than one split picture message.
   1682     private boolean mSplitPictureIsScheduled;
   1683 
   1684     // Used to suspend drawing.
   1685     private boolean mDrawIsPaused;
   1686 
   1687     // mRestoreState is set in didFirstLayout(), and reset in the next
   1688     // webkitDraw after passing it to the UI thread.
   1689     private RestoreState mRestoreState = null;
   1690 
   1691     static class RestoreState {
   1692         float mMinScale;
   1693         float mMaxScale;
   1694         float mViewScale;
   1695         float mTextWrapScale;
   1696         float mDefaultScale;
   1697         int mScrollX;
   1698         int mScrollY;
   1699         boolean mMobileSite;
   1700     }
   1701 
   1702     static class DrawData {
   1703         DrawData() {
   1704             mInvalRegion = new Region();
   1705             mWidthHeight = new Point();
   1706         }
   1707         Region mInvalRegion;
   1708         Point mViewPoint;
   1709         Point mWidthHeight;
   1710         int mMinPrefWidth;
   1711         RestoreState mRestoreState; // only non-null if it is for the first
   1712                                     // picture set after the first layout
   1713         boolean mFocusSizeChanged;
   1714     }
   1715 
   1716     private void webkitDraw() {
   1717         mDrawIsScheduled = false;
   1718         DrawData draw = new DrawData();
   1719         if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
   1720         if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
   1721                 == false) {
   1722             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
   1723             return;
   1724         }
   1725         if (mWebView != null) {
   1726             // Send the native view size that was used during the most recent
   1727             // layout.
   1728             draw.mFocusSizeChanged = nativeFocusBoundsChanged();
   1729             draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
   1730             if (mSettings.getUseWideViewPort()) {
   1731                 draw.mMinPrefWidth = Math.max(
   1732                         mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH
   1733                                 : (mViewportWidth == 0 ? mCurrentViewWidth
   1734                                         : mViewportWidth),
   1735                         nativeGetContentMinPrefWidth());
   1736             }
   1737             if (mRestoreState != null) {
   1738                 draw.mRestoreState = mRestoreState;
   1739                 mRestoreState = null;
   1740             }
   1741             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
   1742             Message.obtain(mWebView.mPrivateHandler,
   1743                     WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
   1744             if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
   1745                 // as we have the new picture, try to sync the scroll position
   1746                 Message.obtain(mWebView.mPrivateHandler,
   1747                         WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
   1748                         mWebkitScrollY).sendToTarget();
   1749                 mWebkitScrollX = mWebkitScrollY = 0;
   1750             }
   1751         }
   1752     }
   1753 
   1754     ///////////////////////////////////////////////////////////////////////////
   1755     // These are called from the UI thread, not our thread
   1756 
   1757     static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
   1758                                          Paint.DITHER_FLAG |
   1759                                          Paint.SUBPIXEL_TEXT_FLAG;
   1760     static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
   1761                                            Paint.DITHER_FLAG;
   1762 
   1763     final DrawFilter mZoomFilter =
   1764                     new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
   1765     // If we need to trade better quality for speed, set mScrollFilter to null
   1766     final DrawFilter mScrollFilter =
   1767                 new PaintFlagsDrawFilter(SCROLL_BITS, 0);
   1768 
   1769     /* package */ void drawContentPicture(Canvas canvas, int color,
   1770                                           boolean animatingZoom,
   1771                                           boolean animatingScroll) {
   1772         DrawFilter df = null;
   1773         if (animatingZoom) {
   1774             df = mZoomFilter;
   1775         } else if (animatingScroll) {
   1776             df = mScrollFilter;
   1777         }
   1778         canvas.setDrawFilter(df);
   1779         boolean tookTooLong = nativeDrawContent(canvas, color);
   1780         canvas.setDrawFilter(null);
   1781         if (tookTooLong && mSplitPictureIsScheduled == false) {
   1782             mSplitPictureIsScheduled = true;
   1783             sendMessage(EventHub.SPLIT_PICTURE_SET);
   1784         }
   1785     }
   1786 
   1787     /* package */ synchronized boolean pictureReady() {
   1788         return 0 != mNativeClass ? nativePictureReady() : false;
   1789     }
   1790 
   1791     /*package*/ synchronized Picture copyContentPicture() {
   1792         Picture result = new Picture();
   1793         if (0 != mNativeClass) {
   1794             nativeCopyContentToPicture(result);
   1795         }
   1796         return result;
   1797     }
   1798 
   1799     static void reducePriority() {
   1800         // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
   1801         sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
   1802         sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
   1803         sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
   1804                 .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
   1805     }
   1806 
   1807     static void resumePriority() {
   1808         // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
   1809         sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
   1810         sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
   1811         sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
   1812                 .obtainMessage(WebCoreThread.RESUME_PRIORITY));
   1813     }
   1814 
   1815     static void pauseUpdatePicture(WebViewCore core) {
   1816         // Note: there is one possible failure mode. If pauseUpdatePicture() is
   1817         // called from UI thread while WEBKIT_DRAW is just pulled out of the
   1818         // queue in WebCore thread to be executed. Then update won't be blocked.
   1819         if (core != null) {
   1820             synchronized (core) {
   1821                 core.mDrawIsPaused = true;
   1822                 if (core.mDrawIsScheduled) {
   1823                     core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
   1824                 }
   1825             }
   1826         }
   1827 
   1828     }
   1829 
   1830     static void resumeUpdatePicture(WebViewCore core) {
   1831         if (core != null) {
   1832             synchronized (core) {
   1833                 core.mDrawIsPaused = false;
   1834                 if (core.mDrawIsScheduled) {
   1835                     core.mDrawIsScheduled = false;
   1836                     core.contentDraw();
   1837                 }
   1838             }
   1839         }
   1840     }
   1841 
   1842     //////////////////////////////////////////////////////////////////////////
   1843 
   1844     private void restoreState(int index) {
   1845         WebBackForwardList list = mCallbackProxy.getBackForwardList();
   1846         int size = list.getSize();
   1847         for (int i = 0; i < size; i++) {
   1848             list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
   1849         }
   1850         mBrowserFrame.mLoadInitFromJava = true;
   1851         list.restoreIndex(mBrowserFrame.mNativeFrame, index);
   1852         mBrowserFrame.mLoadInitFromJava = false;
   1853     }
   1854 
   1855     //-------------------------------------------------------------------------
   1856     // Implement abstract methods in WebViewCore, native WebKit callback part
   1857     //-------------------------------------------------------------------------
   1858 
   1859     // called from JNI or WebView thread
   1860     /* package */ void contentDraw() {
   1861         // don't update the Picture until we have an initial width and finish
   1862         // the first layout
   1863         if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
   1864             return;
   1865         }
   1866         // only fire an event if this is our first request
   1867         synchronized (this) {
   1868             if (mDrawIsScheduled) return;
   1869             mDrawIsScheduled = true;
   1870             if (mDrawIsPaused) return;
   1871             mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
   1872         }
   1873     }
   1874 
   1875     // called by JNI
   1876     private void contentScrollBy(int dx, int dy, boolean animate) {
   1877         if (!mBrowserFrame.firstLayoutDone()) {
   1878             // Will this happen? If yes, we need to do something here.
   1879             return;
   1880         }
   1881         if (mWebView != null) {
   1882             Message msg = Message.obtain(mWebView.mPrivateHandler,
   1883                     WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
   1884             if (mDrawIsScheduled) {
   1885                 mEventHub.sendMessage(Message.obtain(null,
   1886                         EventHub.MESSAGE_RELAY, msg));
   1887             } else {
   1888                 msg.sendToTarget();
   1889             }
   1890         }
   1891     }
   1892 
   1893     // called by JNI
   1894     private void contentScrollTo(int x, int y) {
   1895         if (!mBrowserFrame.firstLayoutDone()) {
   1896             /*
   1897              * WebKit restore state will be called before didFirstLayout(),
   1898              * remember the position as it has to be applied after restoring
   1899              * zoom factor which is controlled by screenWidth.
   1900              */
   1901             mRestoredX = x;
   1902             mRestoredY = y;
   1903             return;
   1904         }
   1905         if (mWebView != null) {
   1906             Message msg = Message.obtain(mWebView.mPrivateHandler,
   1907                     WebView.SCROLL_TO_MSG_ID, x, y);
   1908             if (mDrawIsScheduled) {
   1909                 mEventHub.sendMessage(Message.obtain(null,
   1910                         EventHub.MESSAGE_RELAY, msg));
   1911             } else {
   1912                 msg.sendToTarget();
   1913             }
   1914         }
   1915     }
   1916 
   1917     // called by JNI
   1918     private void contentSpawnScrollTo(int x, int y) {
   1919         if (!mBrowserFrame.firstLayoutDone()) {
   1920             /*
   1921              * WebKit restore state will be called before didFirstLayout(),
   1922              * remember the position as it has to be applied after restoring
   1923              * zoom factor which is controlled by screenWidth.
   1924              */
   1925             mRestoredX = x;
   1926             mRestoredY = y;
   1927             return;
   1928         }
   1929         if (mWebView != null) {
   1930             Message msg = Message.obtain(mWebView.mPrivateHandler,
   1931                     WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
   1932             if (mDrawIsScheduled) {
   1933                 mEventHub.sendMessage(Message.obtain(null,
   1934                         EventHub.MESSAGE_RELAY, msg));
   1935             } else {
   1936                 msg.sendToTarget();
   1937             }
   1938         }
   1939     }
   1940 
   1941     // called by JNI
   1942     private void sendNotifyProgressFinished() {
   1943         sendUpdateTextEntry();
   1944         // as CacheManager can behave based on database transaction, we need to
   1945         // call tick() to trigger endTransaction
   1946         WebViewWorker.getHandler().removeMessages(
   1947                 WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
   1948         WebViewWorker.getHandler().sendEmptyMessage(
   1949                 WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
   1950         contentDraw();
   1951     }
   1952 
   1953     /*  Called by JNI. The coordinates are in doc coordinates, so they need to
   1954         be scaled before they can be used by the view system, which happens
   1955         in WebView since it (and its thread) know the current scale factor.
   1956      */
   1957     private void sendViewInvalidate(int left, int top, int right, int bottom) {
   1958         if (mWebView != null) {
   1959             Message.obtain(mWebView.mPrivateHandler,
   1960                            WebView.INVAL_RECT_MSG_ID,
   1961                            new Rect(left, top, right, bottom)).sendToTarget();
   1962         }
   1963     }
   1964 
   1965     private static boolean mRepaintScheduled = false;
   1966 
   1967     /*
   1968      * Called by the WebView thread
   1969      */
   1970     /* package */ void signalRepaintDone() {
   1971         mRepaintScheduled = false;
   1972     }
   1973 
   1974     // called by JNI
   1975     private void sendImmediateRepaint() {
   1976         if (mWebView != null && !mRepaintScheduled) {
   1977             mRepaintScheduled = true;
   1978             Message.obtain(mWebView.mPrivateHandler,
   1979                            WebView.IMMEDIATE_REPAINT_MSG_ID).sendToTarget();
   1980         }
   1981     }
   1982 
   1983     // called by JNI
   1984     private void setRootLayer(int layer) {
   1985         if (mWebView != null) {
   1986             Message.obtain(mWebView.mPrivateHandler,
   1987                            WebView.SET_ROOT_LAYER_MSG_ID,
   1988                            layer, 0).sendToTarget();
   1989         }
   1990     }
   1991 
   1992     /* package */ WebView getWebView() {
   1993         return mWebView;
   1994     }
   1995 
   1996     private native void setViewportSettingsFromNative();
   1997 
   1998     // called by JNI
   1999     private void didFirstLayout(boolean standardLoad) {
   2000         if (DebugFlags.WEB_VIEW_CORE) {
   2001             Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
   2002         }
   2003 
   2004         mBrowserFrame.didFirstLayout();
   2005 
   2006         if (mWebView == null) return;
   2007 
   2008         boolean updateRestoreState = standardLoad || mRestoredScale > 0;
   2009         setupViewport(updateRestoreState);
   2010         // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will
   2011         // be called after the WebView restore the state. If updateRestoreState
   2012         // is false, start to draw now as it is ready.
   2013         if (!updateRestoreState) {
   2014             mWebView.mViewManager.postReadyToDrawAll();
   2015         }
   2016 
   2017         // reset the scroll position, the restored offset and scales
   2018         mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY
   2019                 = mRestoredScale = mRestoredScreenWidthScale = 0;
   2020     }
   2021 
   2022     // called by JNI
   2023     private void updateViewport() {
   2024         // if updateViewport is called before first layout, wait until first
   2025         // layout to update the viewport. In the rare case, this is called after
   2026         // first layout, force an update as we have just parsed the viewport
   2027         // meta tag.
   2028         if (mBrowserFrame.firstLayoutDone()) {
   2029             setupViewport(true);
   2030         }
   2031     }
   2032 
   2033     private void setupViewport(boolean updateRestoreState) {
   2034         // set the viewport settings from WebKit
   2035         setViewportSettingsFromNative();
   2036 
   2037         // adjust the default scale to match the densityDpi
   2038         float adjust = 1.0f;
   2039         if (mViewportDensityDpi == -1) {
   2040             if (WebView.DEFAULT_SCALE_PERCENT != 100) {
   2041                 adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f;
   2042             }
   2043         } else if (mViewportDensityDpi > 0) {
   2044             adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
   2045                     / mViewportDensityDpi;
   2046         }
   2047         int defaultScale = (int) (adjust * 100);
   2048 
   2049         if (mViewportInitialScale > 0) {
   2050             mViewportInitialScale *= adjust;
   2051         }
   2052         if (mViewportMinimumScale > 0) {
   2053             mViewportMinimumScale *= adjust;
   2054         }
   2055         if (mViewportMaximumScale > 0) {
   2056             mViewportMaximumScale *= adjust;
   2057         }
   2058 
   2059         // infer the values if they are not defined.
   2060         if (mViewportWidth == 0) {
   2061             if (mViewportInitialScale == 0) {
   2062                 mViewportInitialScale = defaultScale;
   2063             }
   2064         }
   2065         if (mViewportUserScalable == false) {
   2066             mViewportInitialScale = defaultScale;
   2067             mViewportMinimumScale = defaultScale;
   2068             mViewportMaximumScale = defaultScale;
   2069         }
   2070         if (mViewportMinimumScale > mViewportInitialScale
   2071                 && mViewportInitialScale != 0) {
   2072             mViewportMinimumScale = mViewportInitialScale;
   2073         }
   2074         if (mViewportMaximumScale > 0
   2075                 && mViewportMaximumScale < mViewportInitialScale) {
   2076             mViewportMaximumScale = mViewportInitialScale;
   2077         }
   2078         if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
   2079             mViewportWidth = 0;
   2080         }
   2081 
   2082         // if mViewportWidth is 0, it means device-width, always update.
   2083         if (mViewportWidth != 0 && !updateRestoreState) {
   2084             RestoreState restoreState = new RestoreState();
   2085             restoreState.mMinScale = mViewportMinimumScale / 100.0f;
   2086             restoreState.mMaxScale = mViewportMaximumScale / 100.0f;
   2087             restoreState.mDefaultScale = adjust;
   2088             // as mViewportWidth is not 0, it is not mobile site.
   2089             restoreState.mMobileSite = false;
   2090             // for non-mobile site, we don't need minPrefWidth, set it as 0
   2091             restoreState.mScrollX = 0;
   2092             Message.obtain(mWebView.mPrivateHandler,
   2093                     WebView.UPDATE_ZOOM_RANGE, restoreState).sendToTarget();
   2094             return;
   2095         }
   2096 
   2097         // now notify webview
   2098         // webViewWidth refers to the width in the view system
   2099         int webViewWidth;
   2100         // viewportWidth refers to the width in the document system
   2101         int viewportWidth = mCurrentViewWidth;
   2102         if (viewportWidth == 0) {
   2103             // this may happen when WebView just starts. This is not perfect as
   2104             // we call WebView method from WebCore thread. But not perfect
   2105             // reference is better than no reference.
   2106             webViewWidth = mWebView.getViewWidth();
   2107             viewportWidth = (int) (webViewWidth / adjust);
   2108             if (viewportWidth == 0) {
   2109                 Log.w(LOGTAG, "Can't get the viewWidth after the first layout");
   2110             }
   2111         } else {
   2112             webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
   2113         }
   2114         mRestoreState = new RestoreState();
   2115         mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
   2116         mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
   2117         mRestoreState.mDefaultScale = adjust;
   2118         mRestoreState.mScrollX = mRestoredX;
   2119         mRestoreState.mScrollY = mRestoredY;
   2120         mRestoreState.mMobileSite = (0 == mViewportWidth);
   2121         if (mRestoredScale > 0) {
   2122             mRestoreState.mViewScale = mRestoredScale / 100.0f;
   2123             if (mRestoredScreenWidthScale > 0) {
   2124                 mRestoreState.mTextWrapScale =
   2125                         mRestoredScreenWidthScale / 100.0f;
   2126             } else {
   2127                 mRestoreState.mTextWrapScale = mRestoreState.mViewScale;
   2128             }
   2129         } else {
   2130             if (mViewportInitialScale > 0) {
   2131                 mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
   2132                         mViewportInitialScale / 100.0f;
   2133             } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
   2134                 mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
   2135                         (float) webViewWidth / mViewportWidth;
   2136             } else {
   2137                 mRestoreState.mTextWrapScale = adjust;
   2138                 // 0 will trigger WebView to turn on zoom overview mode
   2139                 mRestoreState.mViewScale = 0;
   2140             }
   2141         }
   2142 
   2143         if (mWebView.mHeightCanMeasure) {
   2144             // Trick to ensure that the Picture has the exact height for the
   2145             // content by forcing to layout with 0 height after the page is
   2146             // ready, which is indicated by didFirstLayout. This is essential to
   2147             // get rid of the white space in the GMail which uses WebView for
   2148             // message view.
   2149             mWebView.mLastHeightSent = 0;
   2150             // Send a negative scale to indicate that WebCore should reuse
   2151             // the current scale
   2152             WebView.ViewSizeData data = new WebView.ViewSizeData();
   2153             data.mWidth = mWebView.mLastWidthSent;
   2154             data.mHeight = 0;
   2155             // if mHeightCanMeasure is true, getUseWideViewPort() can't be
   2156             // true. It is safe to use mWidth for mTextWrapWidth.
   2157             data.mTextWrapWidth = data.mWidth;
   2158             data.mScale = -1.0f;
   2159             data.mIgnoreHeight = false;
   2160             data.mAnchorX = data.mAnchorY = 0;
   2161             // send VIEW_SIZE_CHANGED to the front of the queue so that we can
   2162             // avoid pushing the wrong picture to the WebView side. If there is
   2163             // a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
   2164             // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED
   2165             // in the queue, as mLastHeightSent has been updated here, we may
   2166             // miss the requestLayout in WebView side after the new picture.
   2167             mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
   2168             mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
   2169                     EventHub.VIEW_SIZE_CHANGED, data));
   2170         } else if (mSettings.getUseWideViewPort()) {
   2171             if (viewportWidth == 0) {
   2172                 // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
   2173                 // to WebViewCore
   2174                 mWebView.mLastWidthSent = 0;
   2175             } else {
   2176                 WebView.ViewSizeData data = new WebView.ViewSizeData();
   2177                 // mViewScale as 0 means it is in zoom overview mode. So we don't
   2178                 // know the exact scale. If mRestoredScale is non-zero, use it;
   2179                 // otherwise just use mTextWrapScale as the initial scale.
   2180                 data.mScale = mRestoreState.mViewScale == 0
   2181                         ? (mRestoredScale > 0 ? mRestoredScale / 100.0f
   2182                                 : mRestoreState.mTextWrapScale)
   2183                         : mRestoreState.mViewScale;
   2184                 if (DebugFlags.WEB_VIEW_CORE) {
   2185                     Log.v(LOGTAG, "setupViewport"
   2186                              + " mRestoredScale=" + mRestoredScale
   2187                              + " mViewScale=" + mRestoreState.mViewScale
   2188                              + " mTextWrapScale=" + mRestoreState.mTextWrapScale
   2189                              );
   2190                 }
   2191                 data.mWidth = Math.round(webViewWidth / data.mScale);
   2192                 // We may get a call here when mCurrentViewHeight == 0 if webcore completes the
   2193                 // first layout before we sync our webview dimensions to it. In that case, we
   2194                 // request the real height of the webview. This is not a perfect solution as we
   2195                 // are calling a WebView method from the WebCore thread. But this is preferable
   2196                 // to syncing an incorrect height.
   2197                 data.mHeight = mCurrentViewHeight == 0 ?
   2198                         Math.round(mWebView.getViewHeight() / data.mScale)
   2199                         : mCurrentViewHeight * data.mWidth / viewportWidth;
   2200                 data.mTextWrapWidth = Math.round(webViewWidth
   2201                         / mRestoreState.mTextWrapScale);
   2202                 data.mIgnoreHeight = false;
   2203                 data.mAnchorX = data.mAnchorY = 0;
   2204                 // send VIEW_SIZE_CHANGED to the front of the queue so that we
   2205                 // can avoid pushing the wrong picture to the WebView side.
   2206                 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
   2207                 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
   2208                         EventHub.VIEW_SIZE_CHANGED, data));
   2209             }
   2210         }
   2211     }
   2212 
   2213     // called by JNI
   2214     private void restoreScale(int scale) {
   2215         if (mBrowserFrame.firstLayoutDone() == false) {
   2216             mRestoredScale = scale;
   2217         }
   2218     }
   2219 
   2220     // called by JNI
   2221     private void restoreScreenWidthScale(int scale) {
   2222         if (!mSettings.getUseWideViewPort()) {
   2223             return;
   2224         }
   2225 
   2226         if (mBrowserFrame.firstLayoutDone() == false) {
   2227             mRestoredScreenWidthScale = scale;
   2228         }
   2229     }
   2230 
   2231     // called by JNI
   2232     private void needTouchEvents(boolean need) {
   2233         if (mWebView != null) {
   2234             Message.obtain(mWebView.mPrivateHandler,
   2235                     WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
   2236                     .sendToTarget();
   2237         }
   2238     }
   2239 
   2240     // called by JNI
   2241     private void updateTextfield(int ptr, boolean changeToPassword,
   2242             String text, int textGeneration) {
   2243         if (mWebView != null) {
   2244             Message msg = Message.obtain(mWebView.mPrivateHandler,
   2245                     WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
   2246                     textGeneration, text);
   2247             msg.getData().putBoolean("password", changeToPassword);
   2248             msg.sendToTarget();
   2249         }
   2250     }
   2251 
   2252     // called by JNI
   2253     private void updateTextSelection(int pointer, int start, int end,
   2254             int textGeneration) {
   2255         if (mWebView != null) {
   2256             Message.obtain(mWebView.mPrivateHandler,
   2257                 WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
   2258                 new TextSelectionData(start, end)).sendToTarget();
   2259         }
   2260     }
   2261 
   2262     // called by JNI
   2263     private void clearTextEntry() {
   2264         if (mWebView == null) return;
   2265         Message.obtain(mWebView.mPrivateHandler,
   2266                 WebView.CLEAR_TEXT_ENTRY).sendToTarget();
   2267     }
   2268 
   2269     // called by JNI
   2270     private void sendFindAgain() {
   2271         if (mWebView == null) return;
   2272         Message.obtain(mWebView.mPrivateHandler,
   2273                 WebView.FIND_AGAIN).sendToTarget();
   2274     }
   2275 
   2276     private native void nativeUpdateFrameCacheIfLoading();
   2277     private native String nativeRequestLabel(int framePtr, int nodePtr);
   2278     /**
   2279      * Scroll the focused textfield to (xPercent, y) in document space
   2280      */
   2281     private native void nativeScrollFocusedTextInput(float xPercent, int y);
   2282 
   2283     // these must be in document space (i.e. not scaled/zoomed).
   2284     private native void nativeSetScrollOffset(int gen, int dx, int dy);
   2285 
   2286     private native void nativeSetGlobalBounds(int x, int y, int w, int h);
   2287 
   2288     // called by JNI
   2289     private void requestListBox(String[] array, int[] enabledArray,
   2290             int[] selectedArray) {
   2291         if (mWebView != null) {
   2292             mWebView.requestListBox(array, enabledArray, selectedArray);
   2293         }
   2294     }
   2295 
   2296     // called by JNI
   2297     private void requestListBox(String[] array, int[] enabledArray,
   2298             int selection) {
   2299         if (mWebView != null) {
   2300             mWebView.requestListBox(array, enabledArray, selection);
   2301         }
   2302 
   2303     }
   2304 
   2305     // called by JNI
   2306     private void requestKeyboardWithSelection(int pointer, int selStart,
   2307             int selEnd, int textGeneration) {
   2308         if (mWebView != null) {
   2309             Message.obtain(mWebView.mPrivateHandler,
   2310                     WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
   2311                     textGeneration, new TextSelectionData(selStart, selEnd))
   2312                     .sendToTarget();
   2313         }
   2314     }
   2315 
   2316     // called by JNI
   2317     private void requestKeyboard(boolean showKeyboard) {
   2318         if (mWebView != null) {
   2319             Message.obtain(mWebView.mPrivateHandler,
   2320                     WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
   2321                     .sendToTarget();
   2322         }
   2323     }
   2324 
   2325     // called by JNI
   2326     private Context getContext() {
   2327         return mContext;
   2328     }
   2329 
   2330     // called by JNI
   2331     private Class<?> getPluginClass(String libName, String clsName) {
   2332 
   2333         if (mWebView == null) {
   2334             return null;
   2335         }
   2336 
   2337         PluginManager pluginManager = PluginManager.getInstance(null);
   2338 
   2339         String pkgName = pluginManager.getPluginsAPKName(libName);
   2340         if (pkgName == null) {
   2341             Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
   2342             return null;
   2343         }
   2344 
   2345         try {
   2346             return pluginManager.getPluginClass(pkgName, clsName);
   2347         } catch (NameNotFoundException e) {
   2348             Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")");
   2349         } catch (ClassNotFoundException e) {
   2350             Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
   2351                     ") in the apk (" + pkgName + ")");
   2352         }
   2353 
   2354         return null;
   2355     }
   2356 
   2357     // called by JNI. PluginWidget function to launch a full-screen view using a
   2358     // View object provided by the plugin class.
   2359     private void showFullScreenPlugin(ViewManager.ChildView childView, int npp) {
   2360         if (mWebView == null) {
   2361             return;
   2362         }
   2363 
   2364         Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN);
   2365         message.obj = childView.mView;
   2366         message.arg1 = npp;
   2367         message.sendToTarget();
   2368     }
   2369 
   2370     // called by JNI
   2371     private void hideFullScreenPlugin() {
   2372         if (mWebView == null) {
   2373             return;
   2374         }
   2375         mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
   2376                 .sendToTarget();
   2377     }
   2378 
   2379     // called by JNI.  PluginWidget functions for creating an embedded View for
   2380     // the surface drawing model.
   2381     private ViewManager.ChildView addSurface(View pluginView, int x, int y,
   2382                                              int width, int height) {
   2383         if (mWebView == null) {
   2384             return null;
   2385         }
   2386 
   2387         if (pluginView == null) {
   2388             Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy");
   2389             return null;
   2390         }
   2391 
   2392         // ensures the view system knows the view can redraw itself
   2393         pluginView.setWillNotDraw(false);
   2394 
   2395         if(pluginView instanceof SurfaceView)
   2396             ((SurfaceView)pluginView).setZOrderOnTop(true);
   2397 
   2398         ViewManager.ChildView view = mWebView.mViewManager.createView();
   2399         view.mView = pluginView;
   2400         view.attachView(x, y, width, height);
   2401         return view;
   2402     }
   2403 
   2404     private void updateSurface(ViewManager.ChildView childView, int x, int y,
   2405             int width, int height) {
   2406         childView.attachView(x, y, width, height);
   2407     }
   2408 
   2409     private void destroySurface(ViewManager.ChildView childView) {
   2410         childView.removeView();
   2411     }
   2412 
   2413     // called by JNI
   2414     static class ShowRectData {
   2415         int mLeft;
   2416         int mTop;
   2417         int mWidth;
   2418         int mHeight;
   2419         int mContentWidth;
   2420         int mContentHeight;
   2421         float mXPercentInDoc;
   2422         float mXPercentInView;
   2423         float mYPercentInDoc;
   2424         float mYPercentInView;
   2425     }
   2426 
   2427     private void showRect(int left, int top, int width, int height,
   2428             int contentWidth, int contentHeight, float xPercentInDoc,
   2429             float xPercentInView, float yPercentInDoc, float yPercentInView) {
   2430         if (mWebView != null) {
   2431             ShowRectData data = new ShowRectData();
   2432             data.mLeft = left;
   2433             data.mTop = top;
   2434             data.mWidth = width;
   2435             data.mHeight = height;
   2436             data.mContentWidth = contentWidth;
   2437             data.mContentHeight = contentHeight;
   2438             data.mXPercentInDoc = xPercentInDoc;
   2439             data.mXPercentInView = xPercentInView;
   2440             data.mYPercentInDoc = yPercentInDoc;
   2441             data.mYPercentInView = yPercentInView;
   2442             Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID,
   2443                     data).sendToTarget();
   2444         }
   2445     }
   2446 
   2447     // called by JNI
   2448     private void centerFitRect(int x, int y, int width, int height) {
   2449         if (mWebView == null) {
   2450             return;
   2451         }
   2452         mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT,
   2453                 new Rect(x, y, x + width, y + height)).sendToTarget();
   2454     }
   2455 
   2456     // called by JNI
   2457     private void setScrollbarModes(int hMode, int vMode) {
   2458         if (mWebView == null) {
   2459             return;
   2460         }
   2461         mWebView.mPrivateHandler.obtainMessage(WebView.SET_SCROLLBAR_MODES,
   2462                 hMode, vMode).sendToTarget();
   2463     }
   2464 
   2465     private native void nativePause();
   2466     private native void nativeResume();
   2467     private native void nativeFreeMemory();
   2468     private native void nativeFullScreenPluginHidden(int npp);
   2469     private native boolean nativeValidNodeAndBounds(int frame, int node,
   2470             Rect bounds);
   2471 
   2472 }
   2473