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