WebViewCore.java revision f5dba888c671c1a4f32a07a4e8f028f7dadd2ce9
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        static final int SET_NETWORK_TYPE = 183;
882
883        // private message ids
884        private static final int DESTROY =     200;
885
886        // Private handler for WebCore messages.
887        private Handler mHandler;
888        // Message queue for containing messages before the WebCore thread is
889        // ready.
890        private ArrayList<Message> mMessages = new ArrayList<Message>();
891        // Flag for blocking messages. This is used during DESTROY to avoid
892        // posting more messages to the EventHub or to WebView's event handler.
893        private boolean mBlockMessages;
894
895        private int mTid;
896        private int mSavedPriority;
897
898        /**
899         * Prevent other classes from creating an EventHub.
900         */
901        private EventHub() {}
902
903        /**
904         * Transfer all messages to the newly created webcore thread handler.
905         */
906        private void transferMessages() {
907            mTid = Process.myTid();
908            mSavedPriority = Process.getThreadPriority(mTid);
909
910            mHandler = new Handler() {
911                @Override
912                public void handleMessage(Message msg) {
913                    if (DebugFlags.WEB_VIEW_CORE) {
914                        Log.v(LOGTAG, (msg.what < REQUEST_LABEL
915                                || msg.what
916                                > VALID_NODE_BOUNDS ? Integer.toString(msg.what)
917                                : HandlerDebugString[msg.what
918                                        - REQUEST_LABEL])
919                                + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
920                                + " obj=" + msg.obj);
921                    }
922                    switch (msg.what) {
923                        case WEBKIT_DRAW:
924                            webkitDraw();
925                            break;
926
927                        case DESTROY:
928                            // Time to take down the world. Cancel all pending
929                            // loads and destroy the native view and frame.
930                            synchronized (WebViewCore.this) {
931                                mBrowserFrame.destroy();
932                                mBrowserFrame = null;
933                                mSettings.onDestroyed();
934                                mNativeClass = 0;
935                                mWebView = null;
936                            }
937                            break;
938
939                        case REQUEST_LABEL:
940                            if (mWebView != null) {
941                                int nodePointer = msg.arg2;
942                                String label = nativeRequestLabel(msg.arg1,
943                                        nodePointer);
944                                if (label != null && label.length() > 0) {
945                                    Message.obtain(mWebView.mPrivateHandler,
946                                            WebView.RETURN_LABEL, nodePointer,
947                                            0, label).sendToTarget();
948                                }
949                            }
950                            break;
951
952                        case UPDATE_FRAME_CACHE_IF_LOADING:
953                            nativeUpdateFrameCacheIfLoading();
954                            break;
955
956                        case SCROLL_TEXT_INPUT:
957                            nativeScrollFocusedTextInput(
958                                    ((Float) msg.obj).floatValue(), msg.arg1);
959                            break;
960
961                        case LOAD_URL:
962                            loadUrl((String) msg.obj);
963                            break;
964
965                        case POST_URL: {
966                            PostUrlData param = (PostUrlData) msg.obj;
967                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
968                            break;
969                        }
970                        case LOAD_DATA:
971                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
972                            String baseUrl = loadParams.mBaseUrl;
973                            if (baseUrl != null) {
974                                int i = baseUrl.indexOf(':');
975                                if (i > 0) {
976                                    /*
977                                     * In 1.0, {@link
978                                     * WebView#loadDataWithBaseURL} can access
979                                     * local asset files as long as the data is
980                                     * valid. In the new WebKit, the restriction
981                                     * is tightened. To be compatible with 1.0,
982                                     * we automatically add the scheme of the
983                                     * baseUrl for local access as long as it is
984                                     * not http(s)/ftp(s)/about/javascript
985                                     */
986                                    String scheme = baseUrl.substring(0, i);
987                                    if (!scheme.startsWith("http") &&
988                                            !scheme.startsWith("ftp") &&
989                                            !scheme.startsWith("about") &&
990                                            !scheme.startsWith("javascript")) {
991                                        nativeRegisterURLSchemeAsLocal(scheme);
992                                    }
993                                }
994                            }
995                            mBrowserFrame.loadData(baseUrl,
996                                    loadParams.mData,
997                                    loadParams.mMimeType,
998                                    loadParams.mEncoding,
999                                    loadParams.mFailUrl);
1000                            break;
1001
1002                        case STOP_LOADING:
1003                            // If the WebCore has committed the load, but not
1004                            // finished the first layout yet, we need to set
1005                            // first layout done to trigger the interpreted side sync
1006                            // up with native side
1007                            if (mBrowserFrame.committed()
1008                                    && !mBrowserFrame.firstLayoutDone()) {
1009                                mBrowserFrame.didFirstLayout();
1010                            }
1011                            // Do this after syncing up the layout state.
1012                            stopLoading();
1013                            break;
1014
1015                        case RELOAD:
1016                            mBrowserFrame.reload(false);
1017                            break;
1018
1019                        case KEY_DOWN:
1020                            key((KeyEvent) msg.obj, true);
1021                            break;
1022
1023                        case KEY_UP:
1024                            key((KeyEvent) msg.obj, false);
1025                            break;
1026
1027                        case CLICK:
1028                            nativeClick(msg.arg1, msg.arg2);
1029                            break;
1030
1031                        case VIEW_SIZE_CHANGED: {
1032                            WebView.ViewSizeData data =
1033                                    (WebView.ViewSizeData) msg.obj;
1034                            viewSizeChanged(data.mWidth, data.mHeight,
1035                                    data.mTextWrapWidth, data.mScale,
1036                                    data.mIgnoreHeight);
1037                            break;
1038                        }
1039                        case SET_SCROLL_OFFSET:
1040                            // note: these are in document coordinates
1041                            // (inv-zoom)
1042                            Point pt = (Point) msg.obj;
1043                            nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
1044                            break;
1045
1046                        case SET_GLOBAL_BOUNDS:
1047                            Rect r = (Rect) msg.obj;
1048                            nativeSetGlobalBounds(r.left, r.top, r.width(),
1049                                r.height());
1050                            break;
1051
1052                        case GO_BACK_FORWARD:
1053                            // If it is a standard load and the load is not
1054                            // committed yet, we interpret BACK as RELOAD
1055                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
1056                                    (mBrowserFrame.loadType() ==
1057                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
1058                                mBrowserFrame.reload(true);
1059                            } else {
1060                                mBrowserFrame.goBackOrForward(msg.arg1);
1061                            }
1062                            break;
1063
1064                        case RESTORE_STATE:
1065                            stopLoading();
1066                            restoreState(msg.arg1);
1067                            break;
1068
1069                        case PAUSE_TIMERS:
1070                            mSavedPriority = Process.getThreadPriority(mTid);
1071                            Process.setThreadPriority(mTid,
1072                                    Process.THREAD_PRIORITY_BACKGROUND);
1073                            pauseTimers();
1074                            if (CacheManager.disableTransaction()) {
1075                                WebCoreThread.mCacheTickersBlocked = true;
1076                                sWebCoreHandler.removeMessages(
1077                                        WebCoreThread.CACHE_TICKER);
1078                            }
1079                            break;
1080
1081                        case RESUME_TIMERS:
1082                            Process.setThreadPriority(mTid, mSavedPriority);
1083                            resumeTimers();
1084                            if (CacheManager.enableTransaction()) {
1085                                WebCoreThread.mCacheTickersBlocked = false;
1086                                sWebCoreHandler.sendMessageDelayed(
1087                                        sWebCoreHandler.obtainMessage(
1088                                        WebCoreThread.CACHE_TICKER),
1089                                        WebCoreThread.CACHE_TICKER_INTERVAL);
1090                            }
1091                            break;
1092
1093                        case ON_PAUSE:
1094                            nativePause();
1095                            break;
1096
1097                        case ON_RESUME:
1098                            nativeResume();
1099                            break;
1100
1101                        case FREE_MEMORY:
1102                            clearCache(false);
1103                            nativeFreeMemory();
1104                            break;
1105
1106                        case SET_NETWORK_STATE:
1107                            if (BrowserFrame.sJavaBridge == null) {
1108                                throw new IllegalStateException("No WebView " +
1109                                        "has been created in this process!");
1110                            }
1111                            BrowserFrame.sJavaBridge
1112                                    .setNetworkOnLine(msg.arg1 == 1);
1113                            break;
1114
1115                        case SET_NETWORK_TYPE:
1116                            if (BrowserFrame.sJavaBridge == null) {
1117                                throw new IllegalStateException("No WebView " +
1118                                        "has been created in this process!");
1119                            }
1120                            Map<String, String> map = (Map<String, String>) msg.obj;
1121                            BrowserFrame.sJavaBridge
1122                                    .setNetworkType(map.get("type"), map.get("subtype"));
1123                            break;
1124
1125                        case CLEAR_CACHE:
1126                            clearCache(msg.arg1 == 1);
1127                            break;
1128
1129                        case CLEAR_HISTORY:
1130                            mCallbackProxy.getBackForwardList().
1131                                    close(mBrowserFrame.mNativeFrame);
1132                            break;
1133
1134                        case REPLACE_TEXT:
1135                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
1136                            nativeReplaceTextfieldText(msg.arg1, msg.arg2,
1137                                    rep.mReplace, rep.mNewStart, rep.mNewEnd,
1138                                    rep.mTextGeneration);
1139                            break;
1140
1141                        case PASS_TO_JS: {
1142                            JSKeyData jsData = (JSKeyData) msg.obj;
1143                            KeyEvent evt = jsData.mEvent;
1144                            int keyCode = evt.getKeyCode();
1145                            int keyValue = evt.getUnicodeChar();
1146                            int generation = msg.arg1;
1147                            passToJs(generation,
1148                                    jsData.mCurrentText,
1149                                    keyCode,
1150                                    keyValue,
1151                                    evt.isDown(),
1152                                    evt.isShiftPressed(), evt.isAltPressed(),
1153                                    evt.isSymPressed());
1154                            break;
1155                        }
1156
1157                        case SAVE_DOCUMENT_STATE: {
1158                            CursorData cDat = (CursorData) msg.obj;
1159                            nativeSaveDocumentState(cDat.mFrame);
1160                            break;
1161                        }
1162
1163                        case CLEAR_SSL_PREF_TABLE:
1164                            Network.getInstance(mContext)
1165                                    .clearUserSslPrefTable();
1166                            break;
1167
1168                        case TOUCH_UP:
1169                            TouchUpData touchUpData = (TouchUpData) msg.obj;
1170                            nativeTouchUp(touchUpData.mMoveGeneration,
1171                                    touchUpData.mFrame, touchUpData.mNode,
1172                                    touchUpData.mX, touchUpData.mY);
1173                            break;
1174
1175                        case TOUCH_EVENT: {
1176                            TouchEventData ted = (TouchEventData) msg.obj;
1177                            Message.obtain(
1178                                    mWebView.mPrivateHandler,
1179                                    WebView.PREVENT_TOUCH_ID, ted.mAction,
1180                                    nativeHandleTouchEvent(ted.mAction, ted.mX,
1181                                            ted.mY)).sendToTarget();
1182                            break;
1183                        }
1184
1185                        case SET_ACTIVE:
1186                            nativeSetFocusControllerActive(msg.arg1 == 1);
1187                            break;
1188
1189                        case ADD_JS_INTERFACE:
1190                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
1191                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
1192                                    jsData.mInterfaceName);
1193                            break;
1194
1195                        case REQUEST_EXT_REPRESENTATION:
1196                            mBrowserFrame.externalRepresentation(
1197                                    (Message) msg.obj);
1198                            break;
1199
1200                        case REQUEST_DOC_AS_TEXT:
1201                            mBrowserFrame.documentAsText((Message) msg.obj);
1202                            break;
1203
1204                        case SET_MOVE_FOCUS:
1205                            CursorData focusData = (CursorData) msg.obj;
1206                            nativeMoveFocus(focusData.mFrame, focusData.mNode);
1207                            break;
1208
1209                        case SET_MOVE_MOUSE:
1210                            CursorData cursorData = (CursorData) msg.obj;
1211                            nativeMoveMouse(cursorData.mFrame,
1212                                     cursorData.mX, cursorData.mY);
1213                            break;
1214
1215                        case SET_MOVE_MOUSE_IF_LATEST:
1216                            CursorData cData = (CursorData) msg.obj;
1217                            nativeMoveMouseIfLatest(cData.mMoveGeneration,
1218                                    cData.mFrame,
1219                                    cData.mX, cData.mY);
1220                            break;
1221
1222                        case REQUEST_CURSOR_HREF: {
1223                            Message hrefMsg = (Message) msg.obj;
1224                            hrefMsg.getData().putString("url",
1225                                    nativeRetrieveHref(msg.arg1, msg.arg2));
1226                            hrefMsg.getData().putString("title",
1227                                    nativeRetrieveAnchorText(msg.arg1, msg.arg2));
1228                            hrefMsg.sendToTarget();
1229                            break;
1230                        }
1231
1232                        case UPDATE_CACHE_AND_TEXT_ENTRY:
1233                            nativeUpdateFrameCache();
1234                            // FIXME: this should provide a minimal rectangle
1235                            if (mWebView != null) {
1236                                mWebView.postInvalidate();
1237                            }
1238                            sendUpdateTextEntry();
1239                            break;
1240
1241                        case DOC_HAS_IMAGES:
1242                            Message imageResult = (Message) msg.obj;
1243                            imageResult.arg1 =
1244                                    mBrowserFrame.documentHasImages() ? 1 : 0;
1245                            imageResult.sendToTarget();
1246                            break;
1247
1248                        case DELETE_SELECTION:
1249                            TextSelectionData deleteSelectionData
1250                                    = (TextSelectionData) msg.obj;
1251                            nativeDeleteSelection(deleteSelectionData.mStart,
1252                                    deleteSelectionData.mEnd, msg.arg1);
1253                            break;
1254
1255                        case SET_SELECTION:
1256                            nativeSetSelection(msg.arg1, msg.arg2);
1257                            break;
1258
1259                        case LISTBOX_CHOICES:
1260                            SparseBooleanArray choices = (SparseBooleanArray)
1261                                    msg.obj;
1262                            int choicesSize = msg.arg1;
1263                            boolean[] choicesArray = new boolean[choicesSize];
1264                            for (int c = 0; c < choicesSize; c++) {
1265                                choicesArray[c] = choices.get(c);
1266                            }
1267                            nativeSendListBoxChoices(choicesArray,
1268                                    choicesSize);
1269                            break;
1270
1271                        case SINGLE_LISTBOX_CHOICE:
1272                            nativeSendListBoxChoice(msg.arg1);
1273                            break;
1274
1275                        case SET_BACKGROUND_COLOR:
1276                            nativeSetBackgroundColor(msg.arg1);
1277                            break;
1278
1279                        case DUMP_DOMTREE:
1280                            nativeDumpDomTree(msg.arg1 == 1);
1281                            break;
1282
1283                        case DUMP_RENDERTREE:
1284                            nativeDumpRenderTree(msg.arg1 == 1);
1285                            break;
1286
1287                        case DUMP_NAVTREE:
1288                            nativeDumpNavTree();
1289                            break;
1290
1291                        case SET_JS_FLAGS:
1292                            nativeSetJsFlags((String)msg.obj);
1293                            break;
1294
1295                        case GEOLOCATION_PERMISSIONS_PROVIDE:
1296                            GeolocationPermissionsData data =
1297                                    (GeolocationPermissionsData) msg.obj;
1298                            nativeGeolocationPermissionsProvide(data.mOrigin,
1299                                    data.mAllow, data.mRemember);
1300                            break;
1301
1302                        case SYNC_SCROLL:
1303                            mWebkitScrollX = msg.arg1;
1304                            mWebkitScrollY = msg.arg2;
1305                            break;
1306
1307                        case SPLIT_PICTURE_SET:
1308                            nativeSplitContent();
1309                            mSplitPictureIsScheduled = false;
1310                            break;
1311
1312                        case CLEAR_CONTENT:
1313                            // Clear the view so that onDraw() will draw nothing
1314                            // but white background
1315                            // (See public method WebView.clearView)
1316                            nativeClearContent();
1317                            break;
1318
1319                        case MESSAGE_RELAY:
1320                            if (msg.obj instanceof Message) {
1321                                ((Message) msg.obj).sendToTarget();
1322                            }
1323                            break;
1324
1325                        case POPULATE_VISITED_LINKS:
1326                            nativeProvideVisitedHistory((String[])msg.obj);
1327                            break;
1328
1329                        case VALID_NODE_BOUNDS: {
1330                            MotionUpData motionUpData = (MotionUpData) msg.obj;
1331                            if (!nativeValidNodeAndBounds(
1332                                    motionUpData.mFrame, motionUpData.mNode,
1333                                    motionUpData.mBounds)) {
1334                                nativeUpdateFrameCache();
1335                            }
1336                            Message message = mWebView.mPrivateHandler
1337                                    .obtainMessage(WebView.DO_MOTION_UP,
1338                                    motionUpData.mX, motionUpData.mY);
1339                            mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(
1340                                    message);
1341                            break;
1342                        }
1343
1344                        case HIDE_FULLSCREEN:
1345                            nativeFullScreenPluginHidden(msg.arg1);
1346                            break;
1347                    }
1348                }
1349            };
1350            // Take all queued messages and resend them to the new handler.
1351            synchronized (this) {
1352                int size = mMessages.size();
1353                for (int i = 0; i < size; i++) {
1354                    mHandler.sendMessage(mMessages.get(i));
1355                }
1356                mMessages = null;
1357            }
1358        }
1359
1360        /**
1361         * Send a message internally to the queue or to the handler
1362         */
1363        private synchronized void sendMessage(Message msg) {
1364            if (mBlockMessages) {
1365                return;
1366            }
1367            if (mMessages != null) {
1368                mMessages.add(msg);
1369            } else {
1370                mHandler.sendMessage(msg);
1371            }
1372        }
1373
1374        private synchronized void removeMessages(int what) {
1375            if (mBlockMessages) {
1376                return;
1377            }
1378            if (what == EventHub.WEBKIT_DRAW) {
1379                mDrawIsScheduled = false;
1380            }
1381            if (mMessages != null) {
1382                Log.w(LOGTAG, "Not supported in this case.");
1383            } else {
1384                mHandler.removeMessages(what);
1385            }
1386        }
1387
1388        private synchronized boolean hasMessages(int what) {
1389            if (mBlockMessages) {
1390                return false;
1391            }
1392            if (mMessages != null) {
1393                Log.w(LOGTAG, "hasMessages() is not supported in this case.");
1394                return false;
1395            } else {
1396                return mHandler.hasMessages(what);
1397            }
1398        }
1399
1400        private synchronized void sendMessageDelayed(Message msg, long delay) {
1401            if (mBlockMessages) {
1402                return;
1403            }
1404            mHandler.sendMessageDelayed(msg, delay);
1405        }
1406
1407        /**
1408         * Send a message internally to the front of the queue.
1409         */
1410        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1411            if (mBlockMessages) {
1412                return;
1413            }
1414            if (mMessages != null) {
1415                mMessages.add(0, msg);
1416            } else {
1417                mHandler.sendMessageAtFrontOfQueue(msg);
1418            }
1419        }
1420
1421        /**
1422         * Remove all the messages.
1423         */
1424        private synchronized void removeMessages() {
1425            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1426            mDrawIsScheduled = false;
1427            mSplitPictureIsScheduled = false;
1428            if (mMessages != null) {
1429                mMessages.clear();
1430            } else {
1431                mHandler.removeCallbacksAndMessages(null);
1432            }
1433        }
1434
1435        /**
1436         * Block sending messages to the EventHub.
1437         */
1438        private synchronized void blockMessages() {
1439            mBlockMessages = true;
1440        }
1441    }
1442
1443    //-------------------------------------------------------------------------
1444    // Methods called by host activity (in the same thread)
1445    //-------------------------------------------------------------------------
1446
1447    void stopLoading() {
1448        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1449        if (mBrowserFrame != null) {
1450            mBrowserFrame.stopLoading();
1451        }
1452    }
1453
1454    //-------------------------------------------------------------------------
1455    // Methods called by WebView
1456    // If it refers to local variable, it needs synchronized().
1457    // If it needs WebCore, it has to send message.
1458    //-------------------------------------------------------------------------
1459
1460    void sendMessage(Message msg) {
1461        mEventHub.sendMessage(msg);
1462    }
1463
1464    void sendMessage(int what) {
1465        mEventHub.sendMessage(Message.obtain(null, what));
1466    }
1467
1468    void sendMessage(int what, Object obj) {
1469        mEventHub.sendMessage(Message.obtain(null, what, obj));
1470    }
1471
1472    void sendMessage(int what, int arg1) {
1473        // just ignore the second argument (make it 0)
1474        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1475    }
1476
1477    void sendMessage(int what, int arg1, int arg2) {
1478        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1479    }
1480
1481    void sendMessage(int what, int arg1, Object obj) {
1482        // just ignore the second argument (make it 0)
1483        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1484    }
1485
1486    void sendMessage(int what, int arg1, int arg2, Object obj) {
1487        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1488    }
1489
1490    void sendMessageAtFrontOfQueue(int what, Object obj) {
1491        mEventHub.sendMessageAtFrontOfQueue(Message.obtain(
1492                null, what, obj));
1493    }
1494
1495    void sendMessageDelayed(int what, Object obj, long delay) {
1496        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1497    }
1498
1499    void removeMessages(int what) {
1500        mEventHub.removeMessages(what);
1501    }
1502
1503    void removeMessages() {
1504        mEventHub.removeMessages();
1505    }
1506
1507    /**
1508     * Removes pending messages and trigger a DESTROY message to send to
1509     * WebCore.
1510     * Called from UI thread.
1511     */
1512    void destroy() {
1513        // We don't want anyone to post a message between removing pending
1514        // messages and sending the destroy message.
1515        synchronized (mEventHub) {
1516            // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
1517            // be preserved even the WebView is destroyed.
1518            // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
1519            boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
1520            boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
1521            mEventHub.removeMessages();
1522            mEventHub.sendMessageAtFrontOfQueue(
1523                    Message.obtain(null, EventHub.DESTROY));
1524            if (hasPause) {
1525                mEventHub.sendMessageAtFrontOfQueue(
1526                        Message.obtain(null, EventHub.PAUSE_TIMERS));
1527            }
1528            if (hasResume) {
1529                mEventHub.sendMessageAtFrontOfQueue(
1530                        Message.obtain(null, EventHub.RESUME_TIMERS));
1531            }
1532            mEventHub.blockMessages();
1533        }
1534    }
1535
1536    //-------------------------------------------------------------------------
1537    // WebViewCore private methods
1538    //-------------------------------------------------------------------------
1539
1540    private void clearCache(boolean includeDiskFiles) {
1541        mBrowserFrame.clearCache();
1542        if (includeDiskFiles) {
1543            CacheManager.removeAllCacheFiles();
1544        }
1545    }
1546
1547    private void loadUrl(String url) {
1548        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1549        mBrowserFrame.loadUrl(url);
1550    }
1551
1552    private void key(KeyEvent evt, boolean isDown) {
1553        if (DebugFlags.WEB_VIEW_CORE) {
1554            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1555                    + evt);
1556        }
1557        int keyCode = evt.getKeyCode();
1558        if (!nativeKey(keyCode, evt.getUnicodeChar(),
1559                evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
1560                evt.isSymPressed(),
1561                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1562            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1563                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1564                if (DebugFlags.WEB_VIEW_CORE) {
1565                    Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
1566                }
1567                if (mWebView != null && evt.isDown()) {
1568                    Message.obtain(mWebView.mPrivateHandler,
1569                            WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
1570                }
1571                return;
1572            }
1573            // bubble up the event handling
1574            // but do not bubble up the ENTER key, which would open the search
1575            // bar without any text.
1576            mCallbackProxy.onUnhandledKeyEvent(evt);
1577        }
1578    }
1579
1580    // These values are used to avoid requesting a layout based on old values
1581    private int mCurrentViewWidth = 0;
1582    private int mCurrentViewHeight = 0;
1583    private float mCurrentViewScale = 1.0f;
1584
1585    // notify webkit that our virtual view size changed size (after inv-zoom)
1586    private void viewSizeChanged(int w, int h, int textwrapWidth, float scale,
1587            boolean ignoreHeight) {
1588        if (DebugFlags.WEB_VIEW_CORE) {
1589            Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
1590                    + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
1591        }
1592        if (w == 0) {
1593            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1594            return;
1595        }
1596        int width = w;
1597        if (mSettings.getUseWideViewPort()) {
1598            if (mViewportWidth == -1) {
1599                if (mSettings.getLayoutAlgorithm() ==
1600                        WebSettings.LayoutAlgorithm.NORMAL) {
1601                    width = WebView.DEFAULT_VIEWPORT_WIDTH;
1602                } else {
1603                    /*
1604                     * if a page's minimum preferred width is wider than the
1605                     * given "w", use it instead to get better layout result. If
1606                     * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1607                     * wider. If we start a page with screen width, due to the
1608                     * delay between {@link #didFirstLayout} and
1609                     * {@link #viewSizeChanged},
1610                     * {@link #nativeGetContentMinPrefWidth} will return a more
1611                     * accurate value than initial 0 to result a better layout.
1612                     * In the worse case, the native width will be adjusted when
1613                     * next zoom or screen orientation change happens.
1614                     */
1615                    width = Math.min(WebView.sMaxViewportWidth, Math.max(w,
1616                            Math.max(WebView.DEFAULT_VIEWPORT_WIDTH,
1617                                    nativeGetContentMinPrefWidth())));
1618                }
1619            } else {
1620                width = Math.max(w, mViewportWidth);
1621            }
1622        }
1623        nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
1624                textwrapWidth, scale, w, h, ignoreHeight);
1625        // Remember the current width and height
1626        boolean needInvalidate = (mCurrentViewWidth == 0);
1627        mCurrentViewWidth = w;
1628        mCurrentViewHeight = h;
1629        mCurrentViewScale = scale;
1630        if (needInvalidate) {
1631            // ensure {@link #webkitDraw} is called as we were blocking in
1632            // {@link #contentDraw} when mCurrentViewWidth is 0
1633            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1634            contentDraw();
1635        }
1636        mEventHub.sendMessage(Message.obtain(null,
1637                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1638    }
1639
1640    private void sendUpdateTextEntry() {
1641        if (mWebView != null) {
1642            Message.obtain(mWebView.mPrivateHandler,
1643                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1644        }
1645    }
1646
1647    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1648    // callbacks. Computes the sum of database quota for all origins.
1649    private long getUsedQuota() {
1650        WebStorage webStorage = WebStorage.getInstance();
1651        Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
1652
1653        if (origins == null) {
1654            return 0;
1655        }
1656        long usedQuota = 0;
1657        for (WebStorage.Origin website : origins) {
1658            usedQuota += website.getQuota();
1659        }
1660        return usedQuota;
1661    }
1662
1663    // Used to avoid posting more than one draw message.
1664    private boolean mDrawIsScheduled;
1665
1666    // Used to avoid posting more than one split picture message.
1667    private boolean mSplitPictureIsScheduled;
1668
1669    // mRestoreState is set in didFirstLayout(), and reset in the next
1670    // webkitDraw after passing it to the UI thread.
1671    private RestoreState mRestoreState = null;
1672
1673    static class RestoreState {
1674        float mMinScale;
1675        float mMaxScale;
1676        float mViewScale;
1677        float mTextWrapScale;
1678        float mDefaultScale;
1679        int mScrollX;
1680        int mScrollY;
1681        boolean mMobileSite;
1682    }
1683
1684    static class DrawData {
1685        DrawData() {
1686            mInvalRegion = new Region();
1687            mWidthHeight = new Point();
1688        }
1689        Region mInvalRegion;
1690        Point mViewPoint;
1691        Point mWidthHeight;
1692        int mMinPrefWidth;
1693        RestoreState mRestoreState; // only non-null if it is for the first
1694                                    // picture set after the first layout
1695        boolean mFocusSizeChanged;
1696    }
1697
1698    private void webkitDraw() {
1699        mDrawIsScheduled = false;
1700        DrawData draw = new DrawData();
1701        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
1702        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
1703                == false) {
1704            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
1705            return;
1706        }
1707        if (mWebView != null) {
1708            // Send the native view size that was used during the most recent
1709            // layout.
1710            draw.mFocusSizeChanged = nativeFocusBoundsChanged();
1711            draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1712            if (mSettings.getUseWideViewPort()) {
1713                draw.mMinPrefWidth = Math.max(
1714                        mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH
1715                                : (mViewportWidth == 0 ? mCurrentViewWidth
1716                                        : mViewportWidth),
1717                        nativeGetContentMinPrefWidth());
1718            }
1719            if (mRestoreState != null) {
1720                draw.mRestoreState = mRestoreState;
1721                mRestoreState = null;
1722            }
1723            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1724            Message.obtain(mWebView.mPrivateHandler,
1725                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
1726            if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1727                // as we have the new picture, try to sync the scroll position
1728                Message.obtain(mWebView.mPrivateHandler,
1729                        WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1730                        mWebkitScrollY).sendToTarget();
1731                mWebkitScrollX = mWebkitScrollY = 0;
1732            }
1733        }
1734    }
1735
1736    ///////////////////////////////////////////////////////////////////////////
1737    // These are called from the UI thread, not our thread
1738
1739    static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
1740                                         Paint.DITHER_FLAG |
1741                                         Paint.SUBPIXEL_TEXT_FLAG;
1742    static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
1743                                           Paint.DITHER_FLAG;
1744
1745    final DrawFilter mZoomFilter =
1746                    new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
1747    final DrawFilter mScrollFilter = null;
1748    // If we need to trade more speed for less quality on slower devices
1749    // use this: new PaintFlagsDrawFilter(SCROLL_BITS, 0);
1750
1751    /* package */ void drawContentPicture(Canvas canvas, int color,
1752                                          boolean animatingZoom,
1753                                          boolean animatingScroll) {
1754        DrawFilter df = null;
1755        if (animatingZoom) {
1756            df = mZoomFilter;
1757        } else if (animatingScroll) {
1758            df = mScrollFilter;
1759        }
1760        canvas.setDrawFilter(df);
1761        boolean tookTooLong = nativeDrawContent(canvas, color);
1762        canvas.setDrawFilter(null);
1763        if (tookTooLong && mSplitPictureIsScheduled == false) {
1764            mSplitPictureIsScheduled = true;
1765            sendMessage(EventHub.SPLIT_PICTURE_SET);
1766        }
1767    }
1768
1769    /* package */ synchronized boolean pictureReady() {
1770        return 0 != mNativeClass ? nativePictureReady() : false;
1771    }
1772
1773    /*package*/ synchronized Picture copyContentPicture() {
1774        Picture result = new Picture();
1775        if (0 != mNativeClass) {
1776            nativeCopyContentToPicture(result);
1777        }
1778        return result;
1779    }
1780
1781    static void pauseUpdate(WebViewCore core) {
1782        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1783        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1784        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1785        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1786                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1787    }
1788
1789    static void resumeUpdate(WebViewCore core) {
1790        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1791        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1792        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1793        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1794                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1795    }
1796
1797    static void startCacheTransaction() {
1798        sWebCoreHandler.sendMessage(sWebCoreHandler
1799                .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
1800    }
1801
1802    static void endCacheTransaction() {
1803        sWebCoreHandler.sendMessage(sWebCoreHandler
1804                .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
1805    }
1806
1807    //////////////////////////////////////////////////////////////////////////
1808
1809    private void restoreState(int index) {
1810        WebBackForwardList list = mCallbackProxy.getBackForwardList();
1811        int size = list.getSize();
1812        for (int i = 0; i < size; i++) {
1813            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1814        }
1815        mBrowserFrame.mLoadInitFromJava = true;
1816        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1817        mBrowserFrame.mLoadInitFromJava = false;
1818    }
1819
1820    //-------------------------------------------------------------------------
1821    // Implement abstract methods in WebViewCore, native WebKit callback part
1822    //-------------------------------------------------------------------------
1823
1824    // called from JNI or WebView thread
1825    /* package */ void contentDraw() {
1826        // don't update the Picture until we have an initial width and finish
1827        // the first layout
1828        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1829            return;
1830        }
1831        // only fire an event if this is our first request
1832        synchronized (this) {
1833            if (mDrawIsScheduled) return;
1834            mDrawIsScheduled = true;
1835            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1836        }
1837    }
1838
1839    // called by JNI
1840    private void contentScrollBy(int dx, int dy, boolean animate) {
1841        if (!mBrowserFrame.firstLayoutDone()) {
1842            // Will this happen? If yes, we need to do something here.
1843            return;
1844        }
1845        if (mWebView != null) {
1846            Message msg = Message.obtain(mWebView.mPrivateHandler,
1847                    WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
1848            if (mDrawIsScheduled) {
1849                mEventHub.sendMessage(Message.obtain(null,
1850                        EventHub.MESSAGE_RELAY, msg));
1851            } else {
1852                msg.sendToTarget();
1853            }
1854        }
1855    }
1856
1857    // called by JNI
1858    private void contentScrollTo(int x, int y) {
1859        if (!mBrowserFrame.firstLayoutDone()) {
1860            /*
1861             * WebKit restore state will be called before didFirstLayout(),
1862             * remember the position as it has to be applied after restoring
1863             * zoom factor which is controlled by screenWidth.
1864             */
1865            mRestoredX = x;
1866            mRestoredY = y;
1867            return;
1868        }
1869        if (mWebView != null) {
1870            Message msg = Message.obtain(mWebView.mPrivateHandler,
1871                    WebView.SCROLL_TO_MSG_ID, x, y);
1872            if (mDrawIsScheduled) {
1873                mEventHub.sendMessage(Message.obtain(null,
1874                        EventHub.MESSAGE_RELAY, msg));
1875            } else {
1876                msg.sendToTarget();
1877            }
1878        }
1879    }
1880
1881    // called by JNI
1882    private void contentSpawnScrollTo(int x, int y) {
1883        if (!mBrowserFrame.firstLayoutDone()) {
1884            /*
1885             * WebKit restore state will be called before didFirstLayout(),
1886             * remember the position as it has to be applied after restoring
1887             * zoom factor which is controlled by screenWidth.
1888             */
1889            mRestoredX = x;
1890            mRestoredY = y;
1891            return;
1892        }
1893        if (mWebView != null) {
1894            Message msg = Message.obtain(mWebView.mPrivateHandler,
1895                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
1896            if (mDrawIsScheduled) {
1897                mEventHub.sendMessage(Message.obtain(null,
1898                        EventHub.MESSAGE_RELAY, msg));
1899            } else {
1900                msg.sendToTarget();
1901            }
1902        }
1903    }
1904
1905    // called by JNI
1906    private void sendNotifyProgressFinished() {
1907        sendUpdateTextEntry();
1908        // as CacheManager can behave based on database transaction, we need to
1909        // call tick() to trigger endTransaction
1910        sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
1911        sWebCoreHandler.sendMessage(sWebCoreHandler
1912                .obtainMessage(WebCoreThread.CACHE_TICKER));
1913        contentDraw();
1914    }
1915
1916    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
1917        be scaled before they can be used by the view system, which happens
1918        in WebView since it (and its thread) know the current scale factor.
1919     */
1920    private void sendViewInvalidate(int left, int top, int right, int bottom) {
1921        if (mWebView != null) {
1922            Message.obtain(mWebView.mPrivateHandler,
1923                           WebView.INVAL_RECT_MSG_ID,
1924                           new Rect(left, top, right, bottom)).sendToTarget();
1925        }
1926    }
1927
1928    private static boolean mRepaintScheduled = false;
1929
1930    /*
1931     * Called by the WebView thread
1932     */
1933    /* package */ void signalRepaintDone() {
1934        mRepaintScheduled = false;
1935    }
1936
1937    // called by JNI
1938    private void sendImmediateRepaint() {
1939        if (mWebView != null && !mRepaintScheduled) {
1940            mRepaintScheduled = true;
1941            Message.obtain(mWebView.mPrivateHandler,
1942                           WebView.IMMEDIATE_REPAINT_MSG_ID).sendToTarget();
1943        }
1944    }
1945
1946    // called by JNI
1947    private void setRootLayer(int layer) {
1948        if (mWebView != null) {
1949            Message.obtain(mWebView.mPrivateHandler,
1950                           WebView.SET_ROOT_LAYER_MSG_ID,
1951                           layer, 0).sendToTarget();
1952        }
1953    }
1954
1955    /* package */ WebView getWebView() {
1956        return mWebView;
1957    }
1958
1959    private native void setViewportSettingsFromNative();
1960
1961    // called by JNI
1962    private void didFirstLayout(boolean standardLoad) {
1963        if (DebugFlags.WEB_VIEW_CORE) {
1964            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
1965        }
1966
1967        mBrowserFrame.didFirstLayout();
1968
1969        if (mWebView == null) return;
1970
1971        boolean updateRestoreState = standardLoad || mRestoredScale > 0;
1972        setupViewport(updateRestoreState);
1973        // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will
1974        // be called after the WebView restore the state. If updateRestoreState
1975        // is false, start to draw now as it is ready.
1976        if (!updateRestoreState) {
1977            mWebView.mViewManager.postReadyToDrawAll();
1978        }
1979
1980        // reset the scroll position, the restored offset and scales
1981        mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY
1982                = mRestoredScale = mRestoredScreenWidthScale = 0;
1983    }
1984
1985    // called by JNI
1986    private void updateViewport() {
1987        // if updateViewport is called before first layout, wait until first
1988        // layout to update the viewport. In the rare case, this is called after
1989        // first layout, force an update as we have just parsed the viewport
1990        // meta tag.
1991        if (mBrowserFrame.firstLayoutDone()) {
1992            setupViewport(true);
1993        }
1994    }
1995
1996    private void setupViewport(boolean updateRestoreState) {
1997        // set the viewport settings from WebKit
1998        setViewportSettingsFromNative();
1999
2000        // adjust the default scale to match the densityDpi
2001        float adjust = 1.0f;
2002        if (mViewportDensityDpi == -1) {
2003            if (WebView.DEFAULT_SCALE_PERCENT != 100) {
2004                adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f;
2005            }
2006        } else if (mViewportDensityDpi > 0) {
2007            adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
2008                    / mViewportDensityDpi;
2009        }
2010        int defaultScale = (int) (adjust * 100);
2011
2012        if (mViewportInitialScale > 0) {
2013            mViewportInitialScale *= adjust;
2014        }
2015        if (mViewportMinimumScale > 0) {
2016            mViewportMinimumScale *= adjust;
2017        }
2018        if (mViewportMaximumScale > 0) {
2019            mViewportMaximumScale *= adjust;
2020        }
2021
2022        // infer the values if they are not defined.
2023        if (mViewportWidth == 0) {
2024            if (mViewportInitialScale == 0) {
2025                mViewportInitialScale = defaultScale;
2026            }
2027        }
2028        if (mViewportUserScalable == false) {
2029            mViewportInitialScale = defaultScale;
2030            mViewportMinimumScale = defaultScale;
2031            mViewportMaximumScale = defaultScale;
2032        }
2033        if (mViewportMinimumScale > mViewportInitialScale
2034                && mViewportInitialScale != 0) {
2035            mViewportMinimumScale = mViewportInitialScale;
2036        }
2037        if (mViewportMaximumScale > 0
2038                && mViewportMaximumScale < mViewportInitialScale) {
2039            mViewportMaximumScale = mViewportInitialScale;
2040        }
2041        if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
2042            mViewportWidth = 0;
2043        }
2044
2045        // if mViewportWidth is 0, it means device-width, always update.
2046        if (mViewportWidth != 0 && !updateRestoreState) return;
2047
2048        // now notify webview
2049        // webViewWidth refers to the width in the view system
2050        int webViewWidth;
2051        // viewportWidth refers to the width in the document system
2052        int viewportWidth = mCurrentViewWidth;
2053        if (viewportWidth == 0) {
2054            // this may happen when WebView just starts. This is not perfect as
2055            // we call WebView method from WebCore thread. But not perfect
2056            // reference is better than no reference.
2057            webViewWidth = mWebView.getViewWidth();
2058            viewportWidth = (int) (webViewWidth / adjust);
2059            if (viewportWidth == 0) {
2060                Log.w(LOGTAG, "Can't get the viewWidth after the first layout");
2061            }
2062        } else {
2063            webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
2064        }
2065        mRestoreState = new RestoreState();
2066        mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
2067        mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
2068        mRestoreState.mDefaultScale = adjust;
2069        mRestoreState.mScrollX = mRestoredX;
2070        mRestoreState.mScrollY = mRestoredY;
2071        mRestoreState.mMobileSite = (0 == mViewportWidth);
2072        if (mRestoredScale > 0) {
2073            if (mRestoredScreenWidthScale > 0) {
2074                mRestoreState.mTextWrapScale =
2075                        mRestoredScreenWidthScale / 100.0f;
2076                // 0 will trigger WebView to turn on zoom overview mode
2077                mRestoreState.mViewScale = 0;
2078            } else {
2079                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
2080                        mRestoredScale / 100.0f;
2081            }
2082        } else {
2083            if (mViewportInitialScale > 0) {
2084                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
2085                        mViewportInitialScale / 100.0f;
2086            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
2087                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
2088                        (float) webViewWidth / mViewportWidth;
2089            } else {
2090                mRestoreState.mTextWrapScale = adjust;
2091                // 0 will trigger WebView to turn on zoom overview mode
2092                mRestoreState.mViewScale = 0;
2093            }
2094        }
2095
2096        if (mWebView.mHeightCanMeasure) {
2097            // Trick to ensure that the Picture has the exact height for the
2098            // content by forcing to layout with 0 height after the page is
2099            // ready, which is indicated by didFirstLayout. This is essential to
2100            // get rid of the white space in the GMail which uses WebView for
2101            // message view.
2102            mWebView.mLastHeightSent = 0;
2103            // Send a negative scale to indicate that WebCore should reuse
2104            // the current scale
2105            WebView.ViewSizeData data = new WebView.ViewSizeData();
2106            data.mWidth = mWebView.mLastWidthSent;
2107            data.mHeight = 0;
2108            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
2109            // true. It is safe to use mWidth for mTextWrapWidth.
2110            data.mTextWrapWidth = data.mWidth;
2111            data.mScale = -1.0f;
2112            data.mIgnoreHeight = false;
2113            // send VIEW_SIZE_CHANGED to the front of the queue so that we can
2114            // avoid pushing the wrong picture to the WebView side. If there is
2115            // a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
2116            // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED
2117            // in the queue, as mLastHeightSent has been updated here, we may
2118            // miss the requestLayout in WebView side after the new picture.
2119            mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2120            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2121                    EventHub.VIEW_SIZE_CHANGED, data));
2122        } else if (mSettings.getUseWideViewPort()) {
2123            if (viewportWidth == 0) {
2124                // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
2125                // to WebViewCore
2126                mWebView.mLastWidthSent = 0;
2127            } else {
2128                WebView.ViewSizeData data = new WebView.ViewSizeData();
2129                // mViewScale as 0 means it is in zoom overview mode. So we don't
2130                // know the exact scale. If mRestoredScale is non-zero, use it;
2131                // otherwise just use mTextWrapScale as the initial scale.
2132                data.mScale = mRestoreState.mViewScale == 0
2133                        ? (mRestoredScale > 0 ? mRestoredScale / 100.0f
2134                                : mRestoreState.mTextWrapScale)
2135                        : mRestoreState.mViewScale;
2136                if (DebugFlags.WEB_VIEW_CORE) {
2137                    Log.v(LOGTAG, "setupViewport"
2138                             + " mRestoredScale=" + mRestoredScale
2139                             + " mViewScale=" + mRestoreState.mViewScale
2140                             + " mTextWrapScale=" + mRestoreState.mTextWrapScale
2141                             );
2142                }
2143                data.mWidth = Math.round(webViewWidth / data.mScale);
2144                data.mHeight = mCurrentViewHeight * data.mWidth / viewportWidth;
2145                data.mTextWrapWidth = Math.round(webViewWidth
2146                        / mRestoreState.mTextWrapScale);
2147                data.mIgnoreHeight = false;
2148                // send VIEW_SIZE_CHANGED to the front of the queue so that we
2149                // can avoid pushing the wrong picture to the WebView side.
2150                mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2151                mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2152                        EventHub.VIEW_SIZE_CHANGED, data));
2153            }
2154        }
2155    }
2156
2157    // called by JNI
2158    private void restoreScale(int scale) {
2159        if (mBrowserFrame.firstLayoutDone() == false) {
2160            mRestoredScale = scale;
2161        }
2162    }
2163
2164    // called by JNI
2165    private void restoreScreenWidthScale(int scale) {
2166        if (!mSettings.getUseWideViewPort()) {
2167            return;
2168        }
2169
2170        if (mBrowserFrame.firstLayoutDone() == false) {
2171            mRestoredScreenWidthScale = scale;
2172        }
2173    }
2174
2175    // called by JNI
2176    private void needTouchEvents(boolean need) {
2177        if (mWebView != null) {
2178            Message.obtain(mWebView.mPrivateHandler,
2179                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
2180                    .sendToTarget();
2181        }
2182    }
2183
2184    // called by JNI
2185    private void updateTextfield(int ptr, boolean changeToPassword,
2186            String text, int textGeneration) {
2187        if (mWebView != null) {
2188            Message msg = Message.obtain(mWebView.mPrivateHandler,
2189                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
2190                    textGeneration, text);
2191            msg.getData().putBoolean("password", changeToPassword);
2192            msg.sendToTarget();
2193        }
2194    }
2195
2196    // called by JNI
2197    private void updateTextSelection(int pointer, int start, int end,
2198            int textGeneration) {
2199        if (mWebView != null) {
2200            Message.obtain(mWebView.mPrivateHandler,
2201                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
2202                new TextSelectionData(start, end)).sendToTarget();
2203        }
2204    }
2205
2206    // called by JNI
2207    private void clearTextEntry() {
2208        if (mWebView == null) return;
2209        Message.obtain(mWebView.mPrivateHandler,
2210                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
2211    }
2212
2213    // called by JNI
2214    private void sendFindAgain() {
2215        if (mWebView == null) return;
2216        Message.obtain(mWebView.mPrivateHandler,
2217                WebView.FIND_AGAIN).sendToTarget();
2218    }
2219
2220    private native void nativeUpdateFrameCacheIfLoading();
2221    private native String nativeRequestLabel(int framePtr, int nodePtr);
2222    /**
2223     * Scroll the focused textfield to (xPercent, y) in document space
2224     */
2225    private native void nativeScrollFocusedTextInput(float xPercent, int y);
2226
2227    // these must be in document space (i.e. not scaled/zoomed).
2228    private native void nativeSetScrollOffset(int gen, int dx, int dy);
2229
2230    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
2231
2232    // called by JNI
2233    private void requestListBox(String[] array, int[] enabledArray,
2234            int[] selectedArray) {
2235        if (mWebView != null) {
2236            mWebView.requestListBox(array, enabledArray, selectedArray);
2237        }
2238    }
2239
2240    // called by JNI
2241    private void requestListBox(String[] array, int[] enabledArray,
2242            int selection) {
2243        if (mWebView != null) {
2244            mWebView.requestListBox(array, enabledArray, selection);
2245        }
2246
2247    }
2248
2249    // called by JNI
2250    private void requestKeyboard(boolean showKeyboard, boolean isTextView) {
2251        if (mWebView != null) {
2252            Message.obtain(mWebView.mPrivateHandler,
2253                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0,
2254                    isTextView ? 1 : 0)
2255                    .sendToTarget();
2256        }
2257    }
2258
2259    // called by JNI
2260    private Context getContext() {
2261        return mContext;
2262    }
2263
2264    // called by JNI
2265    private Class<?> getPluginClass(String libName, String clsName) {
2266
2267        if (mWebView == null) {
2268            return null;
2269        }
2270
2271        PluginManager pluginManager = PluginManager.getInstance(null);
2272
2273        String pkgName = pluginManager.getPluginsAPKName(libName);
2274        if (pkgName == null) {
2275            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2276            return null;
2277        }
2278
2279        try {
2280            return pluginManager.getPluginClass(pkgName, clsName);
2281        } catch (NameNotFoundException e) {
2282            Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")");
2283        } catch (ClassNotFoundException e) {
2284            Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
2285                    ") in the apk (" + pkgName + ")");
2286        }
2287
2288        return null;
2289    }
2290
2291    // called by JNI. PluginWidget function to launch a full-screen view using a
2292    // View object provided by the plugin class.
2293    private void showFullScreenPlugin(ViewManager.ChildView childView,
2294            final int npp, int x, int y, int width, int height) {
2295
2296        if (mWebView == null) {
2297            return;
2298        }
2299
2300        PluginFullScreenData data = new PluginFullScreenData();
2301        data.mView = childView.mView;
2302        data.mNpp = npp;
2303        data.mDocX = x;
2304        data.mDocY = y;
2305        data.mDocWidth = width;
2306        data.mDocHeight = height;
2307        mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN, data)
2308                .sendToTarget();
2309    }
2310
2311    // called by JNI
2312    private void hideFullScreenPlugin() {
2313        if (mWebView == null) {
2314            return;
2315        }
2316        mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
2317                .sendToTarget();
2318    }
2319
2320    // called by JNI
2321    private void updateFullScreenPlugin(int x, int y, int width, int height) {
2322        if (mWebView == null) {
2323            return;
2324        }
2325
2326        PluginFullScreenData data = new PluginFullScreenData();
2327        data.mDocX = x;
2328        data.mDocY = y;
2329        data.mDocWidth = width;
2330        data.mDocHeight = height;
2331        // null mView and mNpp to indicate it is an update
2332        mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN, data)
2333                .sendToTarget();
2334    }
2335
2336    // called by JNI.  PluginWidget functions for creating an embedded View for
2337    // the surface drawing model.
2338    private ViewManager.ChildView addSurface(View pluginView, int x, int y,
2339                                             int width, int height) {
2340        if (mWebView == null) {
2341            return null;
2342        }
2343
2344        if (pluginView == null) {
2345            Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy");
2346            return null;
2347        }
2348
2349        // ensures the view system knows the view can redraw itself
2350        pluginView.setWillNotDraw(false);
2351
2352        ViewManager.ChildView view = mWebView.mViewManager.createView();
2353        view.mView = pluginView;
2354        view.attachView(x, y, width, height);
2355        return view;
2356    }
2357
2358    private void updateSurface(ViewManager.ChildView childView, int x, int y,
2359            int width, int height) {
2360        childView.attachView(x, y, width, height);
2361    }
2362
2363    private void destroySurface(ViewManager.ChildView childView) {
2364        childView.removeView();
2365    }
2366
2367    private native void nativePause();
2368    private native void nativeResume();
2369    private native void nativeFreeMemory();
2370    private native void nativeFullScreenPluginHidden(int npp);
2371    private native boolean nativeValidNodeAndBounds(int frame, int node,
2372            Rect bounds);
2373
2374}
2375