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