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