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