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