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