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