WebViewCore.java revision 6c24b4d10223cb522e6bdbf0e334f61e672f4366
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                                mNativeClass = 0;
857                            }
858                            break;
859
860                        case UPDATE_FRAME_CACHE_IF_LOADING:
861                            nativeUpdateFrameCacheIfLoading();
862                            break;
863
864                        case SCROLL_TEXT_INPUT:
865                            nativeScrollFocusedTextInput(msg.arg1, msg.arg2);
866                            break;
867
868                        case LOAD_URL:
869                            loadUrl((String) msg.obj);
870                            break;
871
872                        case POST_URL: {
873                            PostUrlData param = (PostUrlData) msg.obj;
874                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
875                            break;
876                        }
877                        case LOAD_DATA:
878                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
879                            String baseUrl = loadParams.mBaseUrl;
880                            if (baseUrl != null) {
881                                int i = baseUrl.indexOf(':');
882                                if (i > 0) {
883                                    /*
884                                     * In 1.0, {@link
885                                     * WebView#loadDataWithBaseURL} can access
886                                     * local asset files as long as the data is
887                                     * valid. In the new WebKit, the restriction
888                                     * is tightened. To be compatible with 1.0,
889                                     * we automatically add the scheme of the
890                                     * baseUrl for local access as long as it is
891                                     * not http(s)/ftp(s)/about/javascript
892                                     */
893                                    String scheme = baseUrl.substring(0, i);
894                                    if (!scheme.startsWith("http") &&
895                                            !scheme.startsWith("ftp") &&
896                                            !scheme.startsWith("about") &&
897                                            !scheme.startsWith("javascript")) {
898                                        nativeRegisterURLSchemeAsLocal(scheme);
899                                    }
900                                }
901                            }
902                            mBrowserFrame.loadData(baseUrl,
903                                    loadParams.mData,
904                                    loadParams.mMimeType,
905                                    loadParams.mEncoding,
906                                    loadParams.mFailUrl);
907                            break;
908
909                        case STOP_LOADING:
910                            // If the WebCore has committed the load, but not
911                            // finished the first layout yet, we need to set
912                            // first layout done to trigger the interpreted side sync
913                            // up with native side
914                            if (mBrowserFrame.committed()
915                                    && !mBrowserFrame.firstLayoutDone()) {
916                                mBrowserFrame.didFirstLayout();
917                            }
918                            // Do this after syncing up the layout state.
919                            stopLoading();
920                            break;
921
922                        case RELOAD:
923                            mBrowserFrame.reload(false);
924                            break;
925
926                        case KEY_DOWN:
927                            key((KeyEvent) msg.obj, true);
928                            break;
929
930                        case KEY_UP:
931                            key((KeyEvent) msg.obj, false);
932                            break;
933
934                        case CLICK:
935                            nativeClick(msg.arg1, msg.arg2);
936                            break;
937
938                        case VIEW_SIZE_CHANGED: {
939                            WebView.ViewSizeData data =
940                                    (WebView.ViewSizeData) msg.obj;
941                            viewSizeChanged(data.mWidth, data.mHeight,
942                                    data.mTextWrapWidth, data.mScale,
943                                    data.mIgnoreHeight);
944                            break;
945                        }
946                        case SET_SCROLL_OFFSET:
947                            // note: these are in document coordinates
948                            // (inv-zoom)
949                            Point pt = (Point) msg.obj;
950                            nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
951                            break;
952
953                        case SET_GLOBAL_BOUNDS:
954                            Rect r = (Rect) msg.obj;
955                            nativeSetGlobalBounds(r.left, r.top, r.width(),
956                                r.height());
957                            break;
958
959                        case GO_BACK_FORWARD:
960                            // If it is a standard load and the load is not
961                            // committed yet, we interpret BACK as RELOAD
962                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
963                                    (mBrowserFrame.loadType() ==
964                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
965                                mBrowserFrame.reload(true);
966                            } else {
967                                mBrowserFrame.goBackOrForward(msg.arg1);
968                            }
969                            break;
970
971                        case RESTORE_STATE:
972                            stopLoading();
973                            restoreState(msg.arg1);
974                            break;
975
976                        case PAUSE_TIMERS:
977                            mSavedPriority = Process.getThreadPriority(mTid);
978                            Process.setThreadPriority(mTid,
979                                    Process.THREAD_PRIORITY_BACKGROUND);
980                            pauseTimers();
981                            if (CacheManager.disableTransaction()) {
982                                WebCoreThread.mCacheTickersBlocked = true;
983                                sWebCoreHandler.removeMessages(
984                                        WebCoreThread.CACHE_TICKER);
985                            }
986                            break;
987
988                        case RESUME_TIMERS:
989                            Process.setThreadPriority(mTid, mSavedPriority);
990                            resumeTimers();
991                            if (CacheManager.enableTransaction()) {
992                                WebCoreThread.mCacheTickersBlocked = false;
993                                sWebCoreHandler.sendMessageDelayed(
994                                        sWebCoreHandler.obtainMessage(
995                                        WebCoreThread.CACHE_TICKER),
996                                        WebCoreThread.CACHE_TICKER_INTERVAL);
997                            }
998                            break;
999
1000                        case ON_PAUSE:
1001                            nativePause();
1002                            break;
1003
1004                        case ON_RESUME:
1005                            nativeResume();
1006                            break;
1007
1008                        case FREE_MEMORY:
1009                            clearCache(false);
1010                            nativeFreeMemory();
1011                            break;
1012
1013                        case PLUGIN_STATE:
1014                            PluginStateData psd = (PluginStateData) msg.obj;
1015                            nativeUpdatePluginState(psd.mFrame, psd.mNode, psd.mState);
1016                            break;
1017
1018                        case SET_NETWORK_STATE:
1019                            if (BrowserFrame.sJavaBridge == null) {
1020                                throw new IllegalStateException("No WebView " +
1021                                        "has been created in this process!");
1022                            }
1023                            BrowserFrame.sJavaBridge
1024                                    .setNetworkOnLine(msg.arg1 == 1);
1025                            break;
1026
1027                        case CLEAR_CACHE:
1028                            clearCache(msg.arg1 == 1);
1029                            break;
1030
1031                        case CLEAR_HISTORY:
1032                            mCallbackProxy.getBackForwardList().
1033                                    close(mBrowserFrame.mNativeFrame);
1034                            break;
1035
1036                        case REPLACE_TEXT:
1037                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
1038                            nativeReplaceTextfieldText(msg.arg1, msg.arg2,
1039                                    rep.mReplace, rep.mNewStart, rep.mNewEnd,
1040                                    rep.mTextGeneration);
1041                            break;
1042
1043                        case PASS_TO_JS: {
1044                            JSKeyData jsData = (JSKeyData) msg.obj;
1045                            KeyEvent evt = jsData.mEvent;
1046                            int keyCode = evt.getKeyCode();
1047                            int keyValue = evt.getUnicodeChar();
1048                            int generation = msg.arg1;
1049                            passToJs(generation,
1050                                    jsData.mCurrentText,
1051                                    keyCode,
1052                                    keyValue,
1053                                    evt.isDown(),
1054                                    evt.isShiftPressed(), evt.isAltPressed(),
1055                                    evt.isSymPressed());
1056                            break;
1057                        }
1058
1059                        case SAVE_DOCUMENT_STATE: {
1060                            CursorData cDat = (CursorData) msg.obj;
1061                            nativeSaveDocumentState(cDat.mFrame);
1062                            break;
1063                        }
1064
1065                        case CLEAR_SSL_PREF_TABLE:
1066                            Network.getInstance(mContext)
1067                                    .clearUserSslPrefTable();
1068                            break;
1069
1070                        case TOUCH_UP:
1071                            TouchUpData touchUpData = (TouchUpData) msg.obj;
1072                            nativeTouchUp(touchUpData.mMoveGeneration,
1073                                    touchUpData.mFrame, touchUpData.mNode,
1074                                    touchUpData.mX, touchUpData.mY);
1075                            break;
1076
1077                        case TOUCH_EVENT: {
1078                            TouchEventData ted = (TouchEventData) msg.obj;
1079                            Message.obtain(
1080                                    mWebView.mPrivateHandler,
1081                                    WebView.PREVENT_TOUCH_ID, ted.mAction,
1082                                    nativeHandleTouchEvent(ted.mAction, ted.mX,
1083                                            ted.mY) ? 1 : 0).sendToTarget();
1084                            break;
1085                        }
1086
1087                        case SET_ACTIVE:
1088                            nativeSetFocusControllerActive(msg.arg1 == 1);
1089                            break;
1090
1091                        case ADD_JS_INTERFACE:
1092                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
1093                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
1094                                    jsData.mInterfaceName);
1095                            break;
1096
1097                        case REQUEST_EXT_REPRESENTATION:
1098                            mBrowserFrame.externalRepresentation(
1099                                    (Message) msg.obj);
1100                            break;
1101
1102                        case REQUEST_DOC_AS_TEXT:
1103                            mBrowserFrame.documentAsText((Message) msg.obj);
1104                            break;
1105
1106                        case SET_MOVE_MOUSE:
1107                            CursorData cursorData = (CursorData) msg.obj;
1108                            nativeMoveMouse(cursorData.mFrame,
1109                                     cursorData.mX, cursorData.mY);
1110                            break;
1111
1112                        case SET_MOVE_MOUSE_IF_LATEST:
1113                            CursorData cData = (CursorData) msg.obj;
1114                            nativeMoveMouseIfLatest(cData.mMoveGeneration,
1115                                    cData.mFrame,
1116                                    cData.mX, cData.mY);
1117                            break;
1118
1119                        case REQUEST_CURSOR_HREF: {
1120                            Message hrefMsg = (Message) msg.obj;
1121                            String res = nativeRetrieveHref(msg.arg1, msg.arg2);
1122                            hrefMsg.getData().putString("url", res);
1123                            hrefMsg.sendToTarget();
1124                            break;
1125                        }
1126
1127                        case UPDATE_CACHE_AND_TEXT_ENTRY:
1128                            nativeUpdateFrameCache();
1129                            // FIXME: this should provide a minimal rectangle
1130                            if (mWebView != null) {
1131                                mWebView.postInvalidate();
1132                            }
1133                            sendUpdateTextEntry();
1134                            break;
1135
1136                        case DOC_HAS_IMAGES:
1137                            Message imageResult = (Message) msg.obj;
1138                            imageResult.arg1 =
1139                                    mBrowserFrame.documentHasImages() ? 1 : 0;
1140                            imageResult.sendToTarget();
1141                            break;
1142
1143                        case DELETE_SELECTION:
1144                            TextSelectionData deleteSelectionData
1145                                    = (TextSelectionData) msg.obj;
1146                            nativeDeleteSelection(deleteSelectionData.mStart,
1147                                    deleteSelectionData.mEnd, msg.arg1);
1148                            break;
1149
1150                        case SET_SELECTION:
1151                            nativeSetSelection(msg.arg1, msg.arg2);
1152                            break;
1153
1154                        case LISTBOX_CHOICES:
1155                            SparseBooleanArray choices = (SparseBooleanArray)
1156                                    msg.obj;
1157                            int choicesSize = msg.arg1;
1158                            boolean[] choicesArray = new boolean[choicesSize];
1159                            for (int c = 0; c < choicesSize; c++) {
1160                                choicesArray[c] = choices.get(c);
1161                            }
1162                            nativeSendListBoxChoices(choicesArray,
1163                                    choicesSize);
1164                            break;
1165
1166                        case SINGLE_LISTBOX_CHOICE:
1167                            nativeSendListBoxChoice(msg.arg1);
1168                            break;
1169
1170                        case SET_BACKGROUND_COLOR:
1171                            nativeSetBackgroundColor(msg.arg1);
1172                            break;
1173
1174                        case GET_SELECTION:
1175                            String str = nativeGetSelection((Region) msg.obj);
1176                            Message.obtain(mWebView.mPrivateHandler
1177                                    , WebView.UPDATE_CLIPBOARD, str)
1178                                    .sendToTarget();
1179                            break;
1180
1181                        case DUMP_DOMTREE:
1182                            nativeDumpDomTree(msg.arg1 == 1);
1183                            break;
1184
1185                        case DUMP_RENDERTREE:
1186                            nativeDumpRenderTree(msg.arg1 == 1);
1187                            break;
1188
1189                        case DUMP_NAVTREE:
1190                            nativeDumpNavTree();
1191                            break;
1192
1193                        case SET_JS_FLAGS:
1194                            nativeSetJsFlags((String)msg.obj);
1195                            break;
1196
1197                        case GEOLOCATION_PERMISSIONS_PROVIDE:
1198                            GeolocationPermissionsData data =
1199                                    (GeolocationPermissionsData) msg.obj;
1200                            nativeGeolocationPermissionsProvide(data.mOrigin,
1201                                    data.mAllow, data.mRemember);
1202                            break;
1203
1204                        case SYNC_SCROLL:
1205                            mWebkitScrollX = msg.arg1;
1206                            mWebkitScrollY = msg.arg2;
1207                            break;
1208
1209                        case SPLIT_PICTURE_SET:
1210                            nativeSplitContent();
1211                            mSplitPictureIsScheduled = false;
1212                            break;
1213
1214                        case CLEAR_CONTENT:
1215                            // Clear the view so that onDraw() will draw nothing
1216                            // but white background
1217                            // (See public method WebView.clearView)
1218                            nativeClearContent();
1219                            break;
1220
1221                        case MESSAGE_RELAY:
1222                            if (msg.obj instanceof Message) {
1223                                ((Message) msg.obj).sendToTarget();
1224                            }
1225                            break;
1226                    }
1227                }
1228            };
1229            // Take all queued messages and resend them to the new handler.
1230            synchronized (this) {
1231                int size = mMessages.size();
1232                for (int i = 0; i < size; i++) {
1233                    mHandler.sendMessage(mMessages.get(i));
1234                }
1235                mMessages = null;
1236            }
1237        }
1238
1239        /**
1240         * Send a message internally to the queue or to the handler
1241         */
1242        private synchronized void sendMessage(Message msg) {
1243            if (mBlockMessages) {
1244                return;
1245            }
1246            if (mMessages != null) {
1247                mMessages.add(msg);
1248            } else {
1249                mHandler.sendMessage(msg);
1250            }
1251        }
1252
1253        private synchronized void removeMessages(int what) {
1254            if (mBlockMessages) {
1255                return;
1256            }
1257            if (what == EventHub.WEBKIT_DRAW) {
1258                mDrawIsScheduled = false;
1259            }
1260            if (mMessages != null) {
1261                Log.w(LOGTAG, "Not supported in this case.");
1262            } else {
1263                mHandler.removeMessages(what);
1264            }
1265        }
1266
1267        private synchronized boolean hasMessages(int what) {
1268            if (mBlockMessages) {
1269                return false;
1270            }
1271            if (mMessages != null) {
1272                Log.w(LOGTAG, "hasMessages() is not supported in this case.");
1273                return false;
1274            } else {
1275                return mHandler.hasMessages(what);
1276            }
1277        }
1278
1279        private synchronized void sendMessageDelayed(Message msg, long delay) {
1280            if (mBlockMessages) {
1281                return;
1282            }
1283            mHandler.sendMessageDelayed(msg, delay);
1284        }
1285
1286        /**
1287         * Send a message internally to the front of the queue.
1288         */
1289        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1290            if (mBlockMessages) {
1291                return;
1292            }
1293            if (mMessages != null) {
1294                mMessages.add(0, msg);
1295            } else {
1296                mHandler.sendMessageAtFrontOfQueue(msg);
1297            }
1298        }
1299
1300        /**
1301         * Remove all the messages.
1302         */
1303        private synchronized void removeMessages() {
1304            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1305            mDrawIsScheduled = false;
1306            mSplitPictureIsScheduled = false;
1307            if (mMessages != null) {
1308                mMessages.clear();
1309            } else {
1310                mHandler.removeCallbacksAndMessages(null);
1311            }
1312        }
1313
1314        /**
1315         * Block sending messages to the EventHub.
1316         */
1317        private synchronized void blockMessages() {
1318            mBlockMessages = true;
1319        }
1320    }
1321
1322    //-------------------------------------------------------------------------
1323    // Methods called by host activity (in the same thread)
1324    //-------------------------------------------------------------------------
1325
1326    void stopLoading() {
1327        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1328        if (mBrowserFrame != null) {
1329            mBrowserFrame.stopLoading();
1330        }
1331    }
1332
1333    //-------------------------------------------------------------------------
1334    // Methods called by WebView
1335    // If it refers to local variable, it needs synchronized().
1336    // If it needs WebCore, it has to send message.
1337    //-------------------------------------------------------------------------
1338
1339    void sendMessage(Message msg) {
1340        mEventHub.sendMessage(msg);
1341    }
1342
1343    void sendMessage(int what) {
1344        mEventHub.sendMessage(Message.obtain(null, what));
1345    }
1346
1347    void sendMessage(int what, Object obj) {
1348        mEventHub.sendMessage(Message.obtain(null, what, obj));
1349    }
1350
1351    void sendMessage(int what, int arg1) {
1352        // just ignore the second argument (make it 0)
1353        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1354    }
1355
1356    void sendMessage(int what, int arg1, int arg2) {
1357        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1358    }
1359
1360    void sendMessage(int what, int arg1, Object obj) {
1361        // just ignore the second argument (make it 0)
1362        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1363    }
1364
1365    void sendMessage(int what, int arg1, int arg2, Object obj) {
1366        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1367    }
1368
1369    void sendMessageDelayed(int what, Object obj, long delay) {
1370        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1371    }
1372
1373    void removeMessages(int what) {
1374        mEventHub.removeMessages(what);
1375    }
1376
1377    void removeMessages() {
1378        mEventHub.removeMessages();
1379    }
1380
1381    /**
1382     * Removes pending messages and trigger a DESTROY message to send to
1383     * WebCore.
1384     * Called from UI thread.
1385     */
1386    void destroy() {
1387        // We don't want anyone to post a message between removing pending
1388        // messages and sending the destroy message.
1389        synchronized (mEventHub) {
1390            // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
1391            // be preserved even the WebView is destroyed.
1392            // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
1393            boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
1394            boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
1395            mEventHub.removeMessages();
1396            mEventHub.sendMessageAtFrontOfQueue(
1397                    Message.obtain(null, EventHub.DESTROY));
1398            if (hasPause) {
1399                mEventHub.sendMessageAtFrontOfQueue(
1400                        Message.obtain(null, EventHub.PAUSE_TIMERS));
1401            }
1402            if (hasResume) {
1403                mEventHub.sendMessageAtFrontOfQueue(
1404                        Message.obtain(null, EventHub.RESUME_TIMERS));
1405            }
1406            mEventHub.blockMessages();
1407            mWebView = null;
1408        }
1409    }
1410
1411    //-------------------------------------------------------------------------
1412    // WebViewCore private methods
1413    //-------------------------------------------------------------------------
1414
1415    private void clearCache(boolean includeDiskFiles) {
1416        mBrowserFrame.clearCache();
1417        if (includeDiskFiles) {
1418            CacheManager.removeAllCacheFiles();
1419        }
1420    }
1421
1422    private void loadUrl(String url) {
1423        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1424        mBrowserFrame.loadUrl(url);
1425    }
1426
1427    private void key(KeyEvent evt, boolean isDown) {
1428        if (DebugFlags.WEB_VIEW_CORE) {
1429            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1430                    + evt);
1431        }
1432        int keyCode = evt.getKeyCode();
1433        if (!nativeKey(keyCode, evt.getUnicodeChar(),
1434                evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
1435                evt.isSymPressed(),
1436                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1437            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1438                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1439                if (DebugFlags.WEB_VIEW_CORE) {
1440                    Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
1441                }
1442                if (mWebView != null && evt.isDown()) {
1443                    Message.obtain(mWebView.mPrivateHandler,
1444                            WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
1445                }
1446                return;
1447            }
1448            // bubble up the event handling
1449            // but do not bubble up the ENTER key, which would open the search
1450            // bar without any text.
1451            mCallbackProxy.onUnhandledKeyEvent(evt);
1452        }
1453    }
1454
1455    // These values are used to avoid requesting a layout based on old values
1456    private int mCurrentViewWidth = 0;
1457    private int mCurrentViewHeight = 0;
1458    private float mCurrentViewScale = 1.0f;
1459
1460    // notify webkit that our virtual view size changed size (after inv-zoom)
1461    private void viewSizeChanged(int w, int h, int textwrapWidth, float scale,
1462            boolean ignoreHeight) {
1463        if (DebugFlags.WEB_VIEW_CORE) {
1464            Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
1465                    + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
1466        }
1467        if (w == 0) {
1468            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1469            return;
1470        }
1471        int width = w;
1472        if (mSettings.getUseWideViewPort()) {
1473            if (mViewportWidth == -1) {
1474                if (mSettings.getLayoutAlgorithm() ==
1475                        WebSettings.LayoutAlgorithm.NORMAL) {
1476                    width = DEFAULT_VIEWPORT_WIDTH;
1477                } else {
1478                    /*
1479                     * if a page's minimum preferred width is wider than the
1480                     * given "w", use it instead to get better layout result. If
1481                     * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1482                     * wider. If we start a page with screen width, due to the
1483                     * delay between {@link #didFirstLayout} and
1484                     * {@link #viewSizeChanged},
1485                     * {@link #nativeGetContentMinPrefWidth} will return a more
1486                     * accurate value than initial 0 to result a better layout.
1487                     * In the worse case, the native width will be adjusted when
1488                     * next zoom or screen orientation change happens.
1489                     */
1490                    width = Math.max(w, Math.max(DEFAULT_VIEWPORT_WIDTH,
1491                            nativeGetContentMinPrefWidth()));
1492                }
1493            } else {
1494                width = Math.max(w, mViewportWidth);
1495            }
1496        }
1497        nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
1498                textwrapWidth, scale, w, h, ignoreHeight);
1499        // Remember the current width and height
1500        boolean needInvalidate = (mCurrentViewWidth == 0);
1501        mCurrentViewWidth = w;
1502        mCurrentViewHeight = h;
1503        mCurrentViewScale = scale;
1504        if (needInvalidate) {
1505            // ensure {@link #webkitDraw} is called as we were blocking in
1506            // {@link #contentDraw} when mCurrentViewWidth is 0
1507            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1508            contentDraw();
1509        }
1510        mEventHub.sendMessage(Message.obtain(null,
1511                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1512    }
1513
1514    private void sendUpdateTextEntry() {
1515        if (mWebView != null) {
1516            Message.obtain(mWebView.mPrivateHandler,
1517                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1518        }
1519    }
1520
1521    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1522    // callbacks. Computes the sum of database quota for all origins.
1523    private long getUsedQuota() {
1524        WebStorage webStorage = WebStorage.getInstance();
1525        Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
1526
1527        if (origins == null) {
1528            return 0;
1529        }
1530        long usedQuota = 0;
1531        for (WebStorage.Origin website : origins) {
1532            usedQuota += website.getQuota();
1533        }
1534        return usedQuota;
1535    }
1536
1537    // Used to avoid posting more than one draw message.
1538    private boolean mDrawIsScheduled;
1539
1540    // Used to avoid posting more than one split picture message.
1541    private boolean mSplitPictureIsScheduled;
1542
1543    // Used to suspend drawing.
1544    private boolean mDrawIsPaused;
1545
1546    // mRestoreState is set in didFirstLayout(), and reset in the next
1547    // webkitDraw after passing it to the UI thread.
1548    private RestoreState mRestoreState = null;
1549
1550    static class RestoreState {
1551        float mMinScale;
1552        float mMaxScale;
1553        float mViewScale;
1554        float mTextWrapScale;
1555        float mDefaultScale;
1556        int mScrollX;
1557        int mScrollY;
1558        boolean mMobileSite;
1559    }
1560
1561    static class DrawData {
1562        DrawData() {
1563            mInvalRegion = new Region();
1564            mWidthHeight = new Point();
1565        }
1566        Region mInvalRegion;
1567        Point mViewPoint;
1568        Point mWidthHeight;
1569        int mMinPrefWidth;
1570        RestoreState mRestoreState; // only non-null if it is for the first
1571                                    // picture set after the first layout
1572    }
1573
1574    private void webkitDraw() {
1575        mDrawIsScheduled = false;
1576        DrawData draw = new DrawData();
1577        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
1578        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
1579                == false) {
1580            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
1581            return;
1582        }
1583        if (mWebView != null) {
1584            // Send the native view size that was used during the most recent
1585            // layout.
1586            draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1587            if (mSettings.getUseWideViewPort()) {
1588                draw.mMinPrefWidth = Math.max(
1589                        mViewportWidth == -1 ? DEFAULT_VIEWPORT_WIDTH
1590                                : (mViewportWidth == 0 ? mCurrentViewWidth
1591                                        : mViewportWidth),
1592                        nativeGetContentMinPrefWidth());
1593            }
1594            if (mRestoreState != null) {
1595                draw.mRestoreState = mRestoreState;
1596                mRestoreState = null;
1597            }
1598            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1599            Message.obtain(mWebView.mPrivateHandler,
1600                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
1601            if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1602                // as we have the new picture, try to sync the scroll position
1603                Message.obtain(mWebView.mPrivateHandler,
1604                        WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1605                        mWebkitScrollY).sendToTarget();
1606                mWebkitScrollX = mWebkitScrollY = 0;
1607            }
1608        }
1609    }
1610
1611    ///////////////////////////////////////////////////////////////////////////
1612    // These are called from the UI thread, not our thread
1613
1614    static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
1615                                         Paint.DITHER_FLAG |
1616                                         Paint.SUBPIXEL_TEXT_FLAG;
1617    static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
1618                                           Paint.DITHER_FLAG;
1619
1620    final DrawFilter mZoomFilter =
1621                    new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
1622    final DrawFilter mScrollFilter =
1623                    new PaintFlagsDrawFilter(SCROLL_BITS, 0);
1624
1625    /* package */ void drawContentPicture(Canvas canvas, int color,
1626                                          boolean animatingZoom,
1627                                          boolean animatingScroll) {
1628        DrawFilter df = null;
1629        if (animatingZoom) {
1630            df = mZoomFilter;
1631        } else if (animatingScroll) {
1632            df = mScrollFilter;
1633        }
1634        canvas.setDrawFilter(df);
1635        boolean tookTooLong = nativeDrawContent(canvas, color);
1636        canvas.setDrawFilter(null);
1637        if (tookTooLong && mSplitPictureIsScheduled == false) {
1638            mSplitPictureIsScheduled = true;
1639            sendMessage(EventHub.SPLIT_PICTURE_SET);
1640        }
1641    }
1642
1643    /* package */ synchronized boolean pictureReady() {
1644        return nativePictureReady();
1645    }
1646
1647    /*package*/ synchronized Picture copyContentPicture() {
1648        Picture result = new Picture();
1649        nativeCopyContentToPicture(result);
1650        return result;
1651    }
1652
1653    static void pauseUpdate(WebViewCore core) {
1654        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1655        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1656        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1657        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1658                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1659        // Note: there is one possible failure mode. If pauseUpdate() is called
1660        // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
1661        // of the queue and about to be executed. mDrawIsScheduled may be set to
1662        // false in webkitDraw(). So update won't be blocked. But at least the
1663        // webcore thread priority is still lowered.
1664        if (core != null) {
1665            synchronized (core) {
1666                core.mDrawIsPaused = true;
1667                core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1668            }
1669        }
1670    }
1671
1672    static void resumeUpdate(WebViewCore core) {
1673        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1674        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1675        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1676        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1677                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1678        if (core != null) {
1679            synchronized (core) {
1680                core.mDrawIsScheduled = false;
1681                core.mDrawIsPaused = false;
1682                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate");
1683                core.contentDraw();
1684            }
1685        }
1686    }
1687
1688    static void startCacheTransaction() {
1689        sWebCoreHandler.sendMessage(sWebCoreHandler
1690                .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
1691    }
1692
1693    static void endCacheTransaction() {
1694        sWebCoreHandler.sendMessage(sWebCoreHandler
1695                .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
1696    }
1697
1698    //////////////////////////////////////////////////////////////////////////
1699
1700    private void restoreState(int index) {
1701        WebBackForwardList list = mCallbackProxy.getBackForwardList();
1702        int size = list.getSize();
1703        for (int i = 0; i < size; i++) {
1704            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1705        }
1706        mBrowserFrame.mLoadInitFromJava = true;
1707        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1708        mBrowserFrame.mLoadInitFromJava = false;
1709    }
1710
1711    //-------------------------------------------------------------------------
1712    // Implement abstract methods in WebViewCore, native WebKit callback part
1713    //-------------------------------------------------------------------------
1714
1715    // called from JNI or WebView thread
1716    /* package */ void contentDraw() {
1717        // don't update the Picture until we have an initial width and finish
1718        // the first layout
1719        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1720            return;
1721        }
1722        // only fire an event if this is our first request
1723        synchronized (this) {
1724            if (mDrawIsPaused || mDrawIsScheduled) {
1725                return;
1726            }
1727            mDrawIsScheduled = true;
1728            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1729        }
1730    }
1731
1732    // called by JNI
1733    private void contentScrollBy(int dx, int dy, boolean animate) {
1734        if (!mBrowserFrame.firstLayoutDone()) {
1735            // Will this happen? If yes, we need to do something here.
1736            return;
1737        }
1738        if (mWebView != null) {
1739            Message msg = Message.obtain(mWebView.mPrivateHandler,
1740                    WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
1741            if (mDrawIsScheduled) {
1742                mEventHub.sendMessage(Message.obtain(null,
1743                        EventHub.MESSAGE_RELAY, msg));
1744            } else {
1745                msg.sendToTarget();
1746            }
1747        }
1748    }
1749
1750    // called by JNI
1751    private void contentScrollTo(int x, int y) {
1752        if (!mBrowserFrame.firstLayoutDone()) {
1753            /*
1754             * WebKit restore state will be called before didFirstLayout(),
1755             * remember the position as it has to be applied after restoring
1756             * zoom factor which is controlled by screenWidth.
1757             */
1758            mRestoredX = x;
1759            mRestoredY = y;
1760            return;
1761        }
1762        if (mWebView != null) {
1763            Message msg = Message.obtain(mWebView.mPrivateHandler,
1764                    WebView.SCROLL_TO_MSG_ID, x, y);
1765            if (mDrawIsScheduled) {
1766                mEventHub.sendMessage(Message.obtain(null,
1767                        EventHub.MESSAGE_RELAY, msg));
1768            } else {
1769                msg.sendToTarget();
1770            }
1771        }
1772    }
1773
1774    // called by JNI
1775    private void contentSpawnScrollTo(int x, int y) {
1776        if (!mBrowserFrame.firstLayoutDone()) {
1777            /*
1778             * WebKit restore state will be called before didFirstLayout(),
1779             * remember the position as it has to be applied after restoring
1780             * zoom factor which is controlled by screenWidth.
1781             */
1782            mRestoredX = x;
1783            mRestoredY = y;
1784            return;
1785        }
1786        if (mWebView != null) {
1787            Message msg = Message.obtain(mWebView.mPrivateHandler,
1788                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
1789            if (mDrawIsScheduled) {
1790                mEventHub.sendMessage(Message.obtain(null,
1791                        EventHub.MESSAGE_RELAY, msg));
1792            } else {
1793                msg.sendToTarget();
1794            }
1795        }
1796    }
1797
1798    // called by JNI
1799    private void sendNotifyProgressFinished() {
1800        sendUpdateTextEntry();
1801        // as CacheManager can behave based on database transaction, we need to
1802        // call tick() to trigger endTransaction
1803        sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
1804        sWebCoreHandler.sendMessage(sWebCoreHandler
1805                .obtainMessage(WebCoreThread.CACHE_TICKER));
1806        contentDraw();
1807    }
1808
1809    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
1810        be scaled before they can be used by the view system, which happens
1811        in WebView since it (and its thread) know the current scale factor.
1812     */
1813    private void sendViewInvalidate(int left, int top, int right, int bottom) {
1814        if (mWebView != null) {
1815            Message.obtain(mWebView.mPrivateHandler,
1816                           WebView.INVAL_RECT_MSG_ID,
1817                           new Rect(left, top, right, bottom)).sendToTarget();
1818        }
1819    }
1820
1821    /* package */ WebView getWebView() {
1822        return mWebView;
1823    }
1824
1825    private native void setViewportSettingsFromNative();
1826
1827    // called by JNI
1828    private void didFirstLayout(boolean standardLoad) {
1829        if (DebugFlags.WEB_VIEW_CORE) {
1830            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
1831        }
1832
1833        mBrowserFrame.didFirstLayout();
1834
1835        if (mWebView == null) return;
1836
1837        setupViewport(standardLoad || mRestoredScale > 0);
1838
1839        // reset the scroll position, the restored offset and scales
1840        mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY
1841                = mRestoredScale = mRestoredScreenWidthScale = 0;
1842    }
1843
1844    // called by JNI
1845    private void updateViewport() {
1846        // if updateViewport is called before first layout, wait until first
1847        // layout to update the viewport. In the rare case, this is called after
1848        // first layout, force an update as we have just parsed the viewport
1849        // meta tag.
1850        if (mBrowserFrame.firstLayoutDone()) {
1851            setupViewport(true);
1852        }
1853    }
1854
1855    private void setupViewport(boolean updateRestoreState) {
1856        // set the viewport settings from WebKit
1857        setViewportSettingsFromNative();
1858
1859        // adjust the default scale to match the densityDpi
1860        float adjust = 1.0f;
1861        if (mViewportDensityDpi == -1) {
1862            if (WebView.DEFAULT_SCALE_PERCENT != 100) {
1863                adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f;
1864            }
1865        } else if (mViewportDensityDpi > 0) {
1866            adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
1867                    / mViewportDensityDpi;
1868        }
1869        int defaultScale = (int) (adjust * 100);
1870
1871        if (mViewportInitialScale > 0) {
1872            mViewportInitialScale *= adjust;
1873        }
1874        if (mViewportMinimumScale > 0) {
1875            mViewportMinimumScale *= adjust;
1876        }
1877        if (mViewportMaximumScale > 0) {
1878            mViewportMaximumScale *= adjust;
1879        }
1880
1881        // infer the values if they are not defined.
1882        if (mViewportWidth == 0) {
1883            if (mViewportInitialScale == 0) {
1884                mViewportInitialScale = defaultScale;
1885            }
1886        }
1887        if (mViewportUserScalable == false) {
1888            mViewportInitialScale = defaultScale;
1889            mViewportMinimumScale = defaultScale;
1890            mViewportMaximumScale = defaultScale;
1891        }
1892        if (mViewportMinimumScale > mViewportInitialScale
1893                && mViewportInitialScale != 0) {
1894            mViewportMinimumScale = mViewportInitialScale;
1895        }
1896        if (mViewportMaximumScale > 0
1897                && mViewportMaximumScale < mViewportInitialScale) {
1898            mViewportMaximumScale = mViewportInitialScale;
1899        }
1900        if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
1901            mViewportWidth = 0;
1902        }
1903
1904        // if mViewportWidth is 0, it means device-width, always update.
1905        if (mViewportWidth != 0 && !updateRestoreState) return;
1906
1907        // now notify webview
1908        // webViewWidth refers to the width in the view system
1909        int webViewWidth;
1910        // viewportWidth refers to the width in the document system
1911        int viewportWidth = mCurrentViewWidth;
1912        if (viewportWidth == 0) {
1913            // this may happen when WebView just starts. This is not perfect as
1914            // we call WebView method from WebCore thread. But not perfect
1915            // reference is better than no reference.
1916            webViewWidth = mWebView.getViewWidth();
1917            viewportWidth = (int) (webViewWidth / adjust);
1918            if (viewportWidth == 0) {
1919                Log.w(LOGTAG, "Can't get the viewWidth after the first layout");
1920            }
1921        } else {
1922            webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
1923        }
1924        mRestoreState = new RestoreState();
1925        mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
1926        mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
1927        mRestoreState.mDefaultScale = adjust;
1928        mRestoreState.mScrollX = mRestoredX;
1929        mRestoreState.mScrollY = mRestoredY;
1930        mRestoreState.mMobileSite = (0 == mViewportWidth);
1931        if (mRestoredScale > 0) {
1932            if (mRestoredScreenWidthScale > 0) {
1933                mRestoreState.mTextWrapScale =
1934                        mRestoredScreenWidthScale / 100.0f;
1935                // 0 will trigger WebView to turn on zoom overview mode
1936                mRestoreState.mViewScale = 0;
1937            } else {
1938                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1939                        mRestoredScale / 100.0f;
1940            }
1941        } else {
1942            if (mViewportInitialScale > 0) {
1943                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1944                        mViewportInitialScale / 100.0f;
1945            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
1946                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1947                        (float) webViewWidth / mViewportWidth;
1948            } else {
1949                mRestoreState.mTextWrapScale = adjust;
1950                // 0 will trigger WebView to turn on zoom overview mode
1951                mRestoreState.mViewScale = 0;
1952            }
1953        }
1954
1955        if (mWebView.mHeightCanMeasure) {
1956            // Trick to ensure that the Picture has the exact height for the
1957            // content by forcing to layout with 0 height after the page is
1958            // ready, which is indicated by didFirstLayout. This is essential to
1959            // get rid of the white space in the GMail which uses WebView for
1960            // message view.
1961            mWebView.mLastHeightSent = 0;
1962            // Send a negative scale to indicate that WebCore should reuse
1963            // the current scale
1964            WebView.ViewSizeData data = new WebView.ViewSizeData();
1965            data.mWidth = mWebView.mLastWidthSent;
1966            data.mHeight = 0;
1967            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
1968            // true. It is safe to use mWidth for mTextWrapWidth.
1969            data.mTextWrapWidth = data.mWidth;
1970            data.mScale = -1.0f;
1971            data.mIgnoreHeight = false;
1972            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
1973                    EventHub.VIEW_SIZE_CHANGED, data));
1974        } else if (mSettings.getUseWideViewPort()) {
1975            if (viewportWidth == 0) {
1976                // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
1977                // to WebViewCore
1978                mWebView.mLastWidthSent = 0;
1979            } else {
1980                WebView.ViewSizeData data = new WebView.ViewSizeData();
1981                // mViewScale as 0 means it is in zoom overview mode. So we don't
1982                // know the exact scale. If mRestoredScale is non-zero, use it;
1983                // otherwise just use mTextWrapScale as the initial scale.
1984                data.mScale = mRestoreState.mViewScale == 0
1985                        ? (mRestoredScale > 0 ? mRestoredScale
1986                                : mRestoreState.mTextWrapScale)
1987                        : mRestoreState.mViewScale;
1988                data.mWidth = Math.round(webViewWidth / data.mScale);
1989                data.mHeight = mCurrentViewHeight * data.mWidth / viewportWidth;
1990                data.mTextWrapWidth = Math.round(webViewWidth
1991                        / mRestoreState.mTextWrapScale);
1992                data.mIgnoreHeight = false;
1993                mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
1994                        EventHub.VIEW_SIZE_CHANGED, data));
1995            }
1996        }
1997    }
1998
1999    // called by JNI
2000    private void restoreScale(int scale) {
2001        if (mBrowserFrame.firstLayoutDone() == false) {
2002            mRestoredScale = scale;
2003        }
2004    }
2005
2006    // called by JNI
2007    private void restoreScreenWidthScale(int scale) {
2008        if (!mSettings.getUseWideViewPort()) {
2009            return;
2010        }
2011
2012        if (mBrowserFrame.firstLayoutDone() == false) {
2013            mRestoredScreenWidthScale = scale;
2014        }
2015    }
2016
2017    // called by JNI
2018    private void needTouchEvents(boolean need) {
2019        if (mWebView != null) {
2020            Message.obtain(mWebView.mPrivateHandler,
2021                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
2022                    .sendToTarget();
2023        }
2024    }
2025
2026    // called by JNI
2027    private void updateTextfield(int ptr, boolean changeToPassword,
2028            String text, int textGeneration) {
2029        if (mWebView != null) {
2030            Message msg = Message.obtain(mWebView.mPrivateHandler,
2031                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
2032                    textGeneration, text);
2033            msg.getData().putBoolean("password", changeToPassword);
2034            msg.sendToTarget();
2035        }
2036    }
2037
2038    // called by JNI
2039    private void updateTextSelection(int pointer, int start, int end,
2040            int textGeneration) {
2041        if (mWebView != null) {
2042            Message.obtain(mWebView.mPrivateHandler,
2043                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
2044                new TextSelectionData(start, end)).sendToTarget();
2045        }
2046    }
2047
2048    // called by JNI
2049    private void clearTextEntry() {
2050        if (mWebView == null) return;
2051        Message.obtain(mWebView.mPrivateHandler,
2052                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
2053    }
2054
2055    private native void nativeUpdateFrameCacheIfLoading();
2056
2057    /**
2058     * Scroll the focused textfield to (x, y) in document space
2059     */
2060    private native void nativeScrollFocusedTextInput(int x, int y);
2061
2062    // these must be in document space (i.e. not scaled/zoomed).
2063    private native void nativeSetScrollOffset(int gen, int dx, int dy);
2064
2065    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
2066
2067    // called by JNI
2068    private void requestListBox(String[] array, boolean[] enabledArray,
2069            int[] selectedArray) {
2070        if (mWebView != null) {
2071            mWebView.requestListBox(array, enabledArray, selectedArray);
2072        }
2073    }
2074
2075    // called by JNI
2076    private void requestListBox(String[] array, boolean[] enabledArray,
2077            int selection) {
2078        if (mWebView != null) {
2079            mWebView.requestListBox(array, enabledArray, selection);
2080        }
2081
2082    }
2083
2084    // called by JNI
2085    private void requestKeyboard(boolean showKeyboard) {
2086        if (mWebView != null) {
2087            Message.obtain(mWebView.mPrivateHandler,
2088                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
2089                    .sendToTarget();
2090        }
2091    }
2092
2093    // PluginWidget functions for creating SurfaceViews for the Surface drawing
2094    // model.
2095    private ViewManager.ChildView createSurface(String packageName, String className,
2096            int npp, int x, int y, int width, int height) {
2097        if (mWebView == null) {
2098            return null;
2099        }
2100        PluginStub stub = PluginUtil.getPluginStub(mWebView.getContext(), packageName, className);
2101        if (stub == null) {
2102            Log.e(LOGTAG, "Unable to find plugin class (" + className +
2103                    ") in the apk (" + packageName + ")");
2104            return null;
2105        }
2106
2107        View pluginView = stub.getEmbeddedView(npp, mWebView.getContext());
2108
2109        ViewManager.ChildView view = mWebView.mViewManager.createView();
2110        view.mView = pluginView;
2111        view.attachView(x, y, width, height);
2112        return view;
2113    }
2114
2115    private void destroySurface(ViewManager.ChildView childView) {
2116        childView.removeView();
2117    }
2118
2119    private native void nativePause();
2120    private native void nativeResume();
2121    private native void nativeFreeMemory();
2122}
2123