WebViewCore.java revision 585f13f8dec4cbf55b3bc04d95425d647f0577b2
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                width = Math.max(w, mViewportWidth);
1709            } else {
1710                width = textwrapWidth;
1711            }
1712        }
1713        nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
1714                textwrapWidth, scale, w, h, anchorX, anchorY, ignoreHeight);
1715        // Remember the current width and height
1716        boolean needInvalidate = (mCurrentViewWidth == 0);
1717        mCurrentViewWidth = w;
1718        mCurrentViewHeight = h;
1719        mCurrentViewScale = scale;
1720        if (needInvalidate) {
1721            // ensure {@link #webkitDraw} is called as we were blocking in
1722            // {@link #contentDraw} when mCurrentViewWidth is 0
1723            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1724            contentDraw();
1725        }
1726        mEventHub.sendMessage(Message.obtain(null,
1727                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1728    }
1729
1730    private void sendUpdateTextEntry() {
1731        if (mWebView != null) {
1732            Message.obtain(mWebView.mPrivateHandler,
1733                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1734        }
1735    }
1736
1737    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1738    // callbacks. Computes the sum of database quota for all origins.
1739    private long getUsedQuota() {
1740        WebStorage webStorage = WebStorage.getInstance();
1741        Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
1742
1743        if (origins == null) {
1744            return 0;
1745        }
1746        long usedQuota = 0;
1747        for (WebStorage.Origin website : origins) {
1748            usedQuota += website.getQuota();
1749        }
1750        return usedQuota;
1751    }
1752
1753    // called from UI thread
1754    void splitContent(int content) {
1755        if (!mSplitPictureIsScheduled) {
1756            mSplitPictureIsScheduled = true;
1757            sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
1758        }
1759    }
1760
1761    // Used to avoid posting more than one draw message.
1762    private boolean mDrawIsScheduled;
1763
1764    // Used to avoid posting more than one split picture message.
1765    private boolean mSplitPictureIsScheduled;
1766
1767    // Used to suspend drawing.
1768    private boolean mDrawIsPaused;
1769
1770    // mInitialViewState is set by didFirstLayout() and then reset in the
1771    // next webkitDraw after passing the state to the UI thread.
1772    private ViewState mInitialViewState = null;
1773
1774    static class ViewState {
1775        float mMinScale;
1776        float mMaxScale;
1777        float mViewScale;
1778        float mTextWrapScale;
1779        float mDefaultScale;
1780        int mScrollX;
1781        int mScrollY;
1782        boolean mMobileSite;
1783    }
1784
1785    static class DrawData {
1786        DrawData() {
1787            mBaseLayer = 0;
1788            mInvalRegion = new Region();
1789            mWidthHeight = new Point();
1790        }
1791        int mBaseLayer;
1792        Region mInvalRegion;
1793        Point mViewPoint;
1794        Point mWidthHeight;
1795        int mMinPrefWidth;
1796        // only non-null if it is for the first picture set after the first layout
1797        ViewState mViewState;
1798        boolean mFocusSizeChanged;
1799    }
1800
1801    private void webkitDraw() {
1802        mDrawIsScheduled = false;
1803        DrawData draw = new DrawData();
1804        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
1805        draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight);
1806        if (draw.mBaseLayer == 0) {
1807            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
1808            return;
1809        }
1810        if (mWebView != null) {
1811            // Send the native view size that was used during the most recent
1812            // layout.
1813            draw.mFocusSizeChanged = nativeFocusBoundsChanged();
1814            draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1815            if (mSettings.getUseWideViewPort()) {
1816                draw.mMinPrefWidth = Math.max(
1817                        mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH
1818                                : (mViewportWidth == 0 ? mCurrentViewWidth
1819                                        : mViewportWidth),
1820                        nativeGetContentMinPrefWidth());
1821            }
1822            if (mInitialViewState != null) {
1823                draw.mViewState = mInitialViewState;
1824                mInitialViewState = null;
1825            }
1826            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1827            Message.obtain(mWebView.mPrivateHandler,
1828                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
1829            if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1830                // as we have the new picture, try to sync the scroll position
1831                Message.obtain(mWebView.mPrivateHandler,
1832                        WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1833                        mWebkitScrollY).sendToTarget();
1834                mWebkitScrollX = mWebkitScrollY = 0;
1835            }
1836        }
1837    }
1838
1839    static void reducePriority() {
1840        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1841        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1842        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1843        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1844                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1845    }
1846
1847    static void resumePriority() {
1848        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1849        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1850        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1851        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1852                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1853    }
1854
1855    static void pauseUpdatePicture(WebViewCore core) {
1856        // Note: there is one possible failure mode. If pauseUpdatePicture() is
1857        // called from UI thread while WEBKIT_DRAW is just pulled out of the
1858        // queue in WebCore thread to be executed. Then update won't be blocked.
1859        if (core != null) {
1860            if (!core.getSettings().enableSmoothTransition()) return;
1861
1862            synchronized (core) {
1863                core.mDrawIsPaused = true;
1864                if (core.mDrawIsScheduled) {
1865                    core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1866                }
1867            }
1868        }
1869
1870    }
1871
1872    static void resumeUpdatePicture(WebViewCore core) {
1873        if (core != null) {
1874            // if mDrawIsPaused is true, ignore the setting, continue to resume
1875            if (!core.mDrawIsPaused
1876                    && !core.getSettings().enableSmoothTransition()) return;
1877
1878            synchronized (core) {
1879                core.mDrawIsPaused = false;
1880                // always redraw on resume to reenable gif animations
1881                core.mDrawIsScheduled = false;
1882                core.nativeContentInvalidateAll();
1883                core.contentDraw();
1884            }
1885        }
1886    }
1887
1888    //////////////////////////////////////////////////////////////////////////
1889
1890    private void restoreState(int index) {
1891        WebBackForwardList list = mCallbackProxy.getBackForwardList();
1892        int size = list.getSize();
1893        for (int i = 0; i < size; i++) {
1894            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1895        }
1896        mBrowserFrame.mLoadInitFromJava = true;
1897        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1898        mBrowserFrame.mLoadInitFromJava = false;
1899    }
1900
1901    //-------------------------------------------------------------------------
1902    // Implement abstract methods in WebViewCore, native WebKit callback part
1903    //-------------------------------------------------------------------------
1904
1905    // called from JNI or WebView thread
1906    /* package */ void contentDraw() {
1907        // don't update the Picture until we have an initial width and finish
1908        // the first layout
1909        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1910            return;
1911        }
1912        // only fire an event if this is our first request
1913        synchronized (this) {
1914            if (mDrawIsScheduled) return;
1915            mDrawIsScheduled = true;
1916            if (mDrawIsPaused) return;
1917            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1918        }
1919    }
1920
1921    // called by JNI
1922    private void contentScrollBy(int dx, int dy, boolean animate) {
1923        if (!mBrowserFrame.firstLayoutDone()) {
1924            // Will this happen? If yes, we need to do something here.
1925            return;
1926        }
1927        if (mWebView != null) {
1928            Message msg = Message.obtain(mWebView.mPrivateHandler,
1929                    WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
1930            if (mDrawIsScheduled) {
1931                mEventHub.sendMessage(Message.obtain(null,
1932                        EventHub.MESSAGE_RELAY, msg));
1933            } else {
1934                msg.sendToTarget();
1935            }
1936        }
1937    }
1938
1939    // called by JNI
1940    private void contentScrollTo(int x, int y) {
1941        if (!mBrowserFrame.firstLayoutDone()) {
1942            /*
1943             * WebKit restore state will be called before didFirstLayout(),
1944             * remember the position as it has to be applied after restoring
1945             * zoom factor which is controlled by screenWidth.
1946             */
1947            mRestoredX = x;
1948            mRestoredY = y;
1949            return;
1950        }
1951        if (mWebView != null) {
1952            Message msg = Message.obtain(mWebView.mPrivateHandler,
1953                    WebView.SCROLL_TO_MSG_ID, x, y);
1954            if (mDrawIsScheduled) {
1955                mEventHub.sendMessage(Message.obtain(null,
1956                        EventHub.MESSAGE_RELAY, msg));
1957            } else {
1958                msg.sendToTarget();
1959            }
1960        }
1961    }
1962
1963    // called by JNI
1964    private void contentSpawnScrollTo(int x, int y) {
1965        if (!mBrowserFrame.firstLayoutDone()) {
1966            /*
1967             * WebKit restore state will be called before didFirstLayout(),
1968             * remember the position as it has to be applied after restoring
1969             * zoom factor which is controlled by screenWidth.
1970             */
1971            mRestoredX = x;
1972            mRestoredY = y;
1973            return;
1974        }
1975        if (mWebView != null) {
1976            Message msg = Message.obtain(mWebView.mPrivateHandler,
1977                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
1978            if (mDrawIsScheduled) {
1979                mEventHub.sendMessage(Message.obtain(null,
1980                        EventHub.MESSAGE_RELAY, msg));
1981            } else {
1982                msg.sendToTarget();
1983            }
1984        }
1985    }
1986
1987    // called by JNI
1988    private void sendNotifyProgressFinished() {
1989        sendUpdateTextEntry();
1990        // as CacheManager can behave based on database transaction, we need to
1991        // call tick() to trigger endTransaction
1992        WebViewWorker.getHandler().removeMessages(
1993                WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
1994        WebViewWorker.getHandler().sendEmptyMessage(
1995                WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
1996        contentDraw();
1997    }
1998
1999    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
2000        be scaled before they can be used by the view system, which happens
2001        in WebView since it (and its thread) know the current scale factor.
2002     */
2003    private void sendViewInvalidate(int left, int top, int right, int bottom) {
2004        if (mWebView != null) {
2005            Message.obtain(mWebView.mPrivateHandler,
2006                           WebView.INVAL_RECT_MSG_ID,
2007                           new Rect(left, top, right, bottom)).sendToTarget();
2008        }
2009    }
2010
2011    private static boolean mRepaintScheduled = false;
2012
2013    /*
2014     * Called by the WebView thread
2015     */
2016    /* package */ void signalRepaintDone() {
2017        mRepaintScheduled = false;
2018    }
2019
2020    /* package */ WebView getWebView() {
2021        return mWebView;
2022    }
2023
2024    private native void setViewportSettingsFromNative();
2025
2026    // called by JNI
2027    private void didFirstLayout(boolean standardLoad) {
2028        if (DebugFlags.WEB_VIEW_CORE) {
2029            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
2030        }
2031
2032        mBrowserFrame.didFirstLayout();
2033
2034        if (mWebView == null) return;
2035
2036        boolean updateViewState = standardLoad || mRestoredScale > 0;
2037        setupViewport(updateViewState);
2038        // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will
2039        // be called after the WebView updates its state. If updateRestoreState
2040        // is false, start to draw now as it is ready.
2041        if (!updateViewState) {
2042            mWebView.mViewManager.postReadyToDrawAll();
2043        }
2044
2045        // remove the touch highlight when moving to a new page
2046        if (getSettings().supportTouchOnly()) {
2047            mEventHub.sendMessage(Message.obtain(null,
2048                    EventHub.REMOVE_TOUCH_HIGHLIGHT_RECTS));
2049        }
2050
2051        // reset the scroll position, the restored offset and scales
2052        mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY
2053                = mRestoredScale = mRestoredTextWrapScale = 0;
2054    }
2055
2056    // called by JNI
2057    private void updateViewport() {
2058        // if updateViewport is called before first layout, wait until first
2059        // layout to update the viewport. In the rare case, this is called after
2060        // first layout, force an update as we have just parsed the viewport
2061        // meta tag.
2062        if (mBrowserFrame.firstLayoutDone()) {
2063            setupViewport(true);
2064        }
2065    }
2066
2067    private void setupViewport(boolean updateViewState) {
2068        // set the viewport settings from WebKit
2069        setViewportSettingsFromNative();
2070
2071        // adjust the default scale to match the densityDpi
2072        float adjust = 1.0f;
2073        if (mViewportDensityDpi == -1) {
2074            // convert default zoom scale to a integer (percentage) to avoid any
2075            // issues with floating point comparisons
2076            if (mWebView != null && (int)(mWebView.getDefaultZoomScale() * 100) != 100) {
2077                adjust = mWebView.getDefaultZoomScale();
2078            }
2079        } else if (mViewportDensityDpi > 0) {
2080            adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
2081                    / mViewportDensityDpi;
2082        }
2083        int defaultScale = (int) (adjust * 100);
2084
2085        if (mViewportInitialScale > 0) {
2086            mViewportInitialScale *= adjust;
2087        }
2088        if (mViewportMinimumScale > 0) {
2089            mViewportMinimumScale *= adjust;
2090        }
2091        if (mViewportMaximumScale > 0) {
2092            mViewportMaximumScale *= adjust;
2093        }
2094
2095        // infer the values if they are not defined.
2096        if (mViewportWidth == 0) {
2097            if (mViewportInitialScale == 0) {
2098                mViewportInitialScale = defaultScale;
2099            }
2100        }
2101        if (mViewportUserScalable == false) {
2102            mViewportInitialScale = defaultScale;
2103            mViewportMinimumScale = defaultScale;
2104            mViewportMaximumScale = defaultScale;
2105        }
2106        if (mViewportMinimumScale > mViewportInitialScale
2107                && mViewportInitialScale != 0) {
2108            mViewportMinimumScale = mViewportInitialScale;
2109        }
2110        if (mViewportMaximumScale > 0
2111                && mViewportMaximumScale < mViewportInitialScale) {
2112            mViewportMaximumScale = mViewportInitialScale;
2113        }
2114        if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
2115            mViewportWidth = 0;
2116        }
2117
2118        // if mViewportWidth is 0, it means device-width, always update.
2119        if (mViewportWidth != 0 && !updateViewState) {
2120            ViewState viewState = new ViewState();
2121            viewState.mMinScale = mViewportMinimumScale / 100.0f;
2122            viewState.mMaxScale = mViewportMaximumScale / 100.0f;
2123            viewState.mDefaultScale = adjust;
2124            // as mViewportWidth is not 0, it is not mobile site.
2125            viewState.mMobileSite = false;
2126            // for non-mobile site, we don't need minPrefWidth, set it as 0
2127            viewState.mScrollX = 0;
2128            Message.obtain(mWebView.mPrivateHandler,
2129                    WebView.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
2130            return;
2131        }
2132
2133        // now notify webview
2134        // webViewWidth refers to the width in the view system
2135        int webViewWidth;
2136        // viewportWidth refers to the width in the document system
2137        int viewportWidth = mCurrentViewWidth;
2138        if (viewportWidth == 0) {
2139            // this may happen when WebView just starts. This is not perfect as
2140            // we call WebView method from WebCore thread. But not perfect
2141            // reference is better than no reference.
2142            webViewWidth = mWebView.getViewWidth();
2143            viewportWidth = (int) (webViewWidth / adjust);
2144            if (viewportWidth == 0) {
2145                Log.w(LOGTAG, "Can't get the viewWidth after the first layout");
2146            }
2147        } else {
2148            webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
2149        }
2150        mInitialViewState = new ViewState();
2151        mInitialViewState.mMinScale = mViewportMinimumScale / 100.0f;
2152        mInitialViewState.mMaxScale = mViewportMaximumScale / 100.0f;
2153        mInitialViewState.mDefaultScale = adjust;
2154        mInitialViewState.mScrollX = mRestoredX;
2155        mInitialViewState.mScrollY = mRestoredY;
2156        mInitialViewState.mMobileSite = (0 == mViewportWidth);
2157        if (mRestoredScale > 0) {
2158            mInitialViewState.mViewScale = mRestoredScale / 100.0f;
2159            if (mRestoredTextWrapScale > 0) {
2160                mInitialViewState.mTextWrapScale = mRestoredTextWrapScale / 100.0f;
2161            } else {
2162                mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale;
2163            }
2164        } else {
2165            if (mViewportInitialScale > 0) {
2166                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
2167                        mViewportInitialScale / 100.0f;
2168            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
2169                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
2170                        (float) webViewWidth / mViewportWidth;
2171            } else {
2172                mInitialViewState.mTextWrapScale = adjust;
2173                // 0 will trigger WebView to turn on zoom overview mode
2174                mInitialViewState.mViewScale = 0;
2175            }
2176        }
2177
2178        if (mWebView.mHeightCanMeasure) {
2179            // Trick to ensure that the Picture has the exact height for the
2180            // content by forcing to layout with 0 height after the page is
2181            // ready, which is indicated by didFirstLayout. This is essential to
2182            // get rid of the white space in the GMail which uses WebView for
2183            // message view.
2184            mWebView.mLastHeightSent = 0;
2185            // Send a negative scale to indicate that WebCore should reuse
2186            // the current scale
2187            WebView.ViewSizeData data = new WebView.ViewSizeData();
2188            data.mWidth = mWebView.mLastWidthSent;
2189            data.mHeight = 0;
2190            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
2191            // true. It is safe to use mWidth for mTextWrapWidth.
2192            data.mTextWrapWidth = data.mWidth;
2193            data.mScale = -1.0f;
2194            data.mIgnoreHeight = false;
2195            data.mAnchorX = data.mAnchorY = 0;
2196            // send VIEW_SIZE_CHANGED to the front of the queue so that we can
2197            // avoid pushing the wrong picture to the WebView side. If there is
2198            // a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
2199            // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED
2200            // in the queue, as mLastHeightSent has been updated here, we may
2201            // miss the requestLayout in WebView side after the new picture.
2202            mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2203            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2204                    EventHub.VIEW_SIZE_CHANGED, data));
2205        } else if (mSettings.getUseWideViewPort()) {
2206            if (viewportWidth == 0) {
2207                // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
2208                // to WebViewCore
2209                mWebView.mLastWidthSent = 0;
2210            } else {
2211                WebView.ViewSizeData data = new WebView.ViewSizeData();
2212                // mViewScale as 0 means it is in zoom overview mode. So we don't
2213                // know the exact scale. If mRestoredScale is non-zero, use it;
2214                // otherwise just use mTextWrapScale as the initial scale.
2215                data.mScale = mInitialViewState.mViewScale == 0
2216                        ? (mRestoredScale > 0 ? mRestoredScale / 100.0f
2217                                : mInitialViewState.mTextWrapScale)
2218                        : mInitialViewState.mViewScale;
2219                if (DebugFlags.WEB_VIEW_CORE) {
2220                    Log.v(LOGTAG, "setupViewport"
2221                             + " mRestoredScale=" + mRestoredScale
2222                             + " mViewScale=" + mInitialViewState.mViewScale
2223                             + " mTextWrapScale=" + mInitialViewState.mTextWrapScale
2224                             );
2225                }
2226                data.mWidth = Math.round(webViewWidth / data.mScale);
2227                // We may get a call here when mCurrentViewHeight == 0 if webcore completes the
2228                // first layout before we sync our webview dimensions to it. In that case, we
2229                // request the real height of the webview. This is not a perfect solution as we
2230                // are calling a WebView method from the WebCore thread. But this is preferable
2231                // to syncing an incorrect height.
2232                data.mHeight = mCurrentViewHeight == 0 ?
2233                        Math.round(mWebView.getViewHeight() / data.mScale)
2234                        : mCurrentViewHeight * data.mWidth / viewportWidth;
2235                data.mTextWrapWidth = Math.round(webViewWidth
2236                        / mInitialViewState.mTextWrapScale);
2237                data.mIgnoreHeight = false;
2238                data.mAnchorX = data.mAnchorY = 0;
2239                // send VIEW_SIZE_CHANGED to the front of the queue so that we
2240                // can avoid pushing the wrong picture to the WebView side.
2241                mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2242                mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2243                        EventHub.VIEW_SIZE_CHANGED, data));
2244            }
2245        }
2246    }
2247
2248    // called by JNI
2249    private void restoreScale(int scale, int textWrapScale) {
2250        if (mBrowserFrame.firstLayoutDone() == false) {
2251            mRestoredScale = scale;
2252            if (mSettings.getUseWideViewPort()) {
2253                mRestoredTextWrapScale = textWrapScale;
2254            }
2255        }
2256    }
2257
2258    // called by JNI
2259    private void needTouchEvents(boolean need) {
2260        if (mWebView != null) {
2261            Message.obtain(mWebView.mPrivateHandler,
2262                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
2263                    .sendToTarget();
2264        }
2265    }
2266
2267    // called by JNI
2268    private void updateTextfield(int ptr, boolean changeToPassword,
2269            String text, int textGeneration) {
2270        if (mWebView != null) {
2271            Message msg = Message.obtain(mWebView.mPrivateHandler,
2272                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
2273                    textGeneration, text);
2274            msg.getData().putBoolean("password", changeToPassword);
2275            msg.sendToTarget();
2276        }
2277    }
2278
2279    // called by JNI
2280    private void updateTextSelection(int pointer, int start, int end,
2281            int textGeneration) {
2282        if (mWebView != null) {
2283            Message.obtain(mWebView.mPrivateHandler,
2284                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
2285                new TextSelectionData(start, end)).sendToTarget();
2286        }
2287    }
2288
2289    // called by JNI
2290    private void clearTextEntry() {
2291        if (mWebView == null) return;
2292        Message.obtain(mWebView.mPrivateHandler,
2293                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
2294    }
2295
2296    // called by JNI
2297    private void sendFindAgain() {
2298        if (mWebView == null) return;
2299        Message.obtain(mWebView.mPrivateHandler,
2300                WebView.FIND_AGAIN).sendToTarget();
2301    }
2302
2303    private native void nativeUpdateFrameCacheIfLoading();
2304    private native void nativeRevealSelection();
2305    private native String nativeRequestLabel(int framePtr, int nodePtr);
2306    /**
2307     * Scroll the focused textfield to (xPercent, y) in document space
2308     */
2309    private native void nativeScrollFocusedTextInput(float xPercent, int y);
2310
2311    // these must be in document space (i.e. not scaled/zoomed).
2312    private native void nativeSetScrollOffset(int gen, int dx, int dy);
2313
2314    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
2315
2316    // called by JNI
2317    private void requestListBox(String[] array, int[] enabledArray,
2318            int[] selectedArray) {
2319        if (mWebView != null) {
2320            mWebView.requestListBox(array, enabledArray, selectedArray);
2321        }
2322    }
2323
2324    // called by JNI
2325    private void requestListBox(String[] array, int[] enabledArray,
2326            int selection) {
2327        if (mWebView != null) {
2328            mWebView.requestListBox(array, enabledArray, selection);
2329        }
2330
2331    }
2332
2333    // called by JNI
2334    private void requestKeyboardWithSelection(int pointer, int selStart,
2335            int selEnd, int textGeneration) {
2336        if (mWebView != null) {
2337            Message.obtain(mWebView.mPrivateHandler,
2338                    WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
2339                    textGeneration, new TextSelectionData(selStart, selEnd))
2340                    .sendToTarget();
2341        }
2342    }
2343
2344    // called by JNI
2345    private void requestKeyboard(boolean showKeyboard) {
2346        if (mWebView != null) {
2347            Message.obtain(mWebView.mPrivateHandler,
2348                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
2349                    .sendToTarget();
2350        }
2351    }
2352
2353    // called by JNI
2354    private Context getContext() {
2355        return mContext;
2356    }
2357
2358    // called by JNI
2359    private Class<?> getPluginClass(String libName, String clsName) {
2360
2361        if (mWebView == null) {
2362            return null;
2363        }
2364
2365        PluginManager pluginManager = PluginManager.getInstance(null);
2366
2367        String pkgName = pluginManager.getPluginsAPKName(libName);
2368        if (pkgName == null) {
2369            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2370            return null;
2371        }
2372
2373        try {
2374            return pluginManager.getPluginClass(pkgName, clsName);
2375        } catch (NameNotFoundException e) {
2376            Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")");
2377        } catch (ClassNotFoundException e) {
2378            Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
2379                    ") in the apk (" + pkgName + ")");
2380        }
2381
2382        return null;
2383    }
2384
2385    // called by JNI. PluginWidget function to launch a full-screen view using a
2386    // View object provided by the plugin class.
2387    private void showFullScreenPlugin(ViewManager.ChildView childView, int npp) {
2388        if (mWebView == null) {
2389            return;
2390        }
2391
2392        Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN);
2393        message.obj = childView.mView;
2394        message.arg1 = npp;
2395        message.sendToTarget();
2396    }
2397
2398    // called by JNI
2399    private void hideFullScreenPlugin() {
2400        if (mWebView == null) {
2401            return;
2402        }
2403        mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
2404                .sendToTarget();
2405    }
2406
2407    // called by JNI.  PluginWidget functions for creating an embedded View for
2408    // the surface drawing model.
2409    private ViewManager.ChildView addSurface(View pluginView, int x, int y,
2410                                             int width, int height) {
2411        if (mWebView == null) {
2412            return null;
2413        }
2414
2415        if (pluginView == null) {
2416            Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy");
2417            return null;
2418        }
2419
2420        // ensures the view system knows the view can redraw itself
2421        pluginView.setWillNotDraw(false);
2422
2423        if(pluginView instanceof SurfaceView)
2424            ((SurfaceView)pluginView).setZOrderOnTop(true);
2425
2426        ViewManager.ChildView view = mWebView.mViewManager.createView();
2427        view.mView = pluginView;
2428        view.attachView(x, y, width, height);
2429        return view;
2430    }
2431
2432    private void updateSurface(ViewManager.ChildView childView, int x, int y,
2433            int width, int height) {
2434        childView.attachView(x, y, width, height);
2435    }
2436
2437    private void destroySurface(ViewManager.ChildView childView) {
2438        childView.removeView();
2439    }
2440
2441    // called by JNI
2442    static class ShowRectData {
2443        int mLeft;
2444        int mTop;
2445        int mWidth;
2446        int mHeight;
2447        int mContentWidth;
2448        int mContentHeight;
2449        float mXPercentInDoc;
2450        float mXPercentInView;
2451        float mYPercentInDoc;
2452        float mYPercentInView;
2453    }
2454
2455    private void showRect(int left, int top, int width, int height,
2456            int contentWidth, int contentHeight, float xPercentInDoc,
2457            float xPercentInView, float yPercentInDoc, float yPercentInView) {
2458        if (mWebView != null) {
2459            ShowRectData data = new ShowRectData();
2460            data.mLeft = left;
2461            data.mTop = top;
2462            data.mWidth = width;
2463            data.mHeight = height;
2464            data.mContentWidth = contentWidth;
2465            data.mContentHeight = contentHeight;
2466            data.mXPercentInDoc = xPercentInDoc;
2467            data.mXPercentInView = xPercentInView;
2468            data.mYPercentInDoc = yPercentInDoc;
2469            data.mYPercentInView = yPercentInView;
2470            Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID,
2471                    data).sendToTarget();
2472        }
2473    }
2474
2475    // called by JNI
2476    private void centerFitRect(int x, int y, int width, int height) {
2477        if (mWebView == null) {
2478            return;
2479        }
2480        mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT,
2481                new Rect(x, y, x + width, y + height)).sendToTarget();
2482    }
2483
2484    // called by JNI
2485    private void setScrollbarModes(int hMode, int vMode) {
2486        if (mWebView == null) {
2487            return;
2488        }
2489        mWebView.mPrivateHandler.obtainMessage(WebView.SET_SCROLLBAR_MODES,
2490                hMode, vMode).sendToTarget();
2491    }
2492
2493    private void useMockDeviceOrientation() {
2494        mDeviceOrientationManager.useMock();
2495    }
2496
2497    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
2498            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
2499        mDeviceOrientationManager.setMockOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
2500                canProvideGamma, gamma);
2501    }
2502
2503    protected DeviceOrientationService getDeviceOrientationService() {
2504        if (mDeviceOrientationService == null) {
2505            mDeviceOrientationService =
2506                    new DeviceOrientationService(mDeviceOrientationManager, mContext);
2507        }
2508        return mDeviceOrientationService;
2509    }
2510
2511    private native void nativePause();
2512    private native void nativeResume();
2513    private native void nativeFreeMemory();
2514    private native void nativeFullScreenPluginHidden(int npp);
2515    private native boolean nativeValidNodeAndBounds(int frame, int node,
2516            Rect bounds);
2517
2518    private native ArrayList<Rect> nativeGetTouchHighlightRects(int x, int y,
2519            int slop);
2520}
2521