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