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