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