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