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