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