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