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