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