WebViewCore.java revision 0ac031b3d29c6de90895c875991585812dc7388f
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.graphics.Canvas;
21import android.graphics.DrawFilter;
22import android.graphics.Paint;
23import android.graphics.PaintFlagsDrawFilter;
24import android.graphics.Picture;
25import android.graphics.Point;
26import android.graphics.Rect;
27import android.graphics.Region;
28import android.os.Handler;
29import android.os.Looper;
30import android.os.Message;
31import android.os.Process;
32import android.util.Log;
33import android.util.SparseBooleanArray;
34import android.view.KeyEvent;
35import android.view.SurfaceHolder;
36import android.view.SurfaceView;
37
38import java.util.ArrayList;
39import java.util.Set;
40
41import junit.framework.Assert;
42
43final class WebViewCore {
44
45    private static final String LOGTAG = "webcore";
46
47    static {
48        // Load libwebcore during static initialization. This happens in the
49        // zygote process so it will be shared read-only across all app
50        // processes.
51        System.loadLibrary("webcore");
52    }
53
54    /*
55     * WebViewCore always executes in the same thread as the native webkit.
56     */
57
58    // The WebView that corresponds to this WebViewCore.
59    private WebView mWebView;
60    // Proxy for handling callbacks from native code
61    private final CallbackProxy mCallbackProxy;
62    // Settings object for maintaining all settings
63    private final WebSettings mSettings;
64    // Context for initializing the BrowserFrame with the proper assets.
65    private final Context mContext;
66    // The pointer to a native view object.
67    private int mNativeClass;
68    // The BrowserFrame is an interface to the native Frame component.
69    private BrowserFrame mBrowserFrame;
70
71    /*
72     * range is from 200 to 10,000. 0 is a special value means device-width. -1
73     * means undefined.
74     */
75    private int mViewportWidth = -1;
76
77    /*
78     * range is from 200 to 10,000. 0 is a special value means device-height. -1
79     * means undefined.
80     */
81    private int mViewportHeight = -1;
82
83    /*
84     * scale in percent, range is from 1 to 1000. 0 means undefined.
85     */
86    private int mViewportInitialScale = 0;
87
88    /*
89     * scale in percent, range is from 1 to 1000. 0 means undefined.
90     */
91    private int mViewportMinimumScale = 0;
92
93    /*
94     * scale in percent, range is from 1 to 1000. 0 means undefined.
95     */
96    private int mViewportMaximumScale = 0;
97
98    private boolean mViewportUserScalable = true;
99
100    private int mRestoredScale = 0;
101    private int mRestoredScreenWidthScale = 0;
102    private int mRestoredX = 0;
103    private int mRestoredY = 0;
104
105    private int mWebkitScrollX = 0;
106    private int mWebkitScrollY = 0;
107
108    // The thread name used to identify the WebCore thread and for use in
109    // debugging other classes that require operation within the WebCore thread.
110    /* package */ static final String THREAD_NAME = "WebViewCoreThread";
111
112    public WebViewCore(Context context, WebView w, CallbackProxy proxy) {
113        // No need to assign this in the WebCore thread.
114        mCallbackProxy = proxy;
115        mWebView = w;
116        // This context object is used to initialize the WebViewCore during
117        // subwindow creation.
118        mContext = context;
119
120        // We need to wait for the initial thread creation before sending
121        // a message to the WebCore thread.
122        // XXX: This is the only time the UI thread will wait for the WebCore
123        // thread!
124        synchronized (WebViewCore.class) {
125            if (sWebCoreHandler == null) {
126                // Create a global thread and start it.
127                Thread t = new Thread(new WebCoreThread());
128                t.setName(THREAD_NAME);
129                t.start();
130                try {
131                    WebViewCore.class.wait();
132                } catch (InterruptedException e) {
133                    Log.e(LOGTAG, "Caught exception while waiting for thread " +
134                           "creation.");
135                    Log.e(LOGTAG, Log.getStackTraceString(e));
136                }
137            }
138        }
139        // Create an EventHub to handle messages before and after the thread is
140        // ready.
141        mEventHub = new EventHub();
142        // Create a WebSettings object for maintaining all settings
143        mSettings = new WebSettings(mContext, mWebView);
144        // The WebIconDatabase needs to be initialized within the UI thread so
145        // just request the instance here.
146        WebIconDatabase.getInstance();
147        // Create the WebStorage singleton
148        WebStorage.getInstance();
149        // Send a message to initialize the WebViewCore.
150        Message init = sWebCoreHandler.obtainMessage(
151                WebCoreThread.INITIALIZE, this);
152        sWebCoreHandler.sendMessage(init);
153    }
154
155    /* Initialize private data within the WebCore thread.
156     */
157    private void initialize() {
158        /* Initialize our private BrowserFrame class to handle all
159         * frame-related functions. We need to create a new view which
160         * in turn creates a C level FrameView and attaches it to the frame.
161         */
162        mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy,
163                mSettings);
164        // Sync the native settings and also create the WebCore thread handler.
165        mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
166        // Create the handler and transfer messages for the IconDatabase
167        WebIconDatabase.getInstance().createHandler();
168        // Create the handler for WebStorage
169        WebStorage.getInstance().createHandler();
170        // Create the handler for GeolocationPermissions.
171        GeolocationPermissions.getInstance().createHandler();
172        // The transferMessages call will transfer all pending messages to the
173        // WebCore thread handler.
174        mEventHub.transferMessages();
175
176        // Send a message back to WebView to tell it that we have set up the
177        // WebCore thread.
178        if (mWebView != null) {
179            Message.obtain(mWebView.mPrivateHandler,
180                    WebView.WEBCORE_INITIALIZED_MSG_ID,
181                    mNativeClass, 0).sendToTarget();
182        }
183
184    }
185
186    /* Handle the initialization of WebViewCore during subwindow creation. This
187     * method is called from the WebCore thread but it is called before the
188     * INITIALIZE message can be handled.
189     */
190    /* package */ void initializeSubwindow() {
191        // Go ahead and initialize the core components.
192        initialize();
193        // Remove the INITIALIZE method so we don't try to initialize twice.
194        sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this);
195    }
196
197    /* Get the BrowserFrame component. This is used for subwindow creation and
198     * is called only from BrowserFrame in the WebCore thread. */
199    /* package */ BrowserFrame getBrowserFrame() {
200        return mBrowserFrame;
201    }
202
203    //-------------------------------------------------------------------------
204    // Common methods
205    //-------------------------------------------------------------------------
206
207    /**
208     * Causes all timers to pause. This applies to all WebViews in the current
209     * app process.
210     */
211    public static void pauseTimers() {
212        if (BrowserFrame.sJavaBridge == null) {
213            throw new IllegalStateException(
214                    "No WebView has been created in this process!");
215        }
216        BrowserFrame.sJavaBridge.pause();
217    }
218
219    /**
220     * Resume all timers. This applies to all WebViews in the current process.
221     */
222    public static void resumeTimers() {
223        if (BrowserFrame.sJavaBridge == null) {
224            throw new IllegalStateException(
225                    "No WebView has been created in this process!");
226        }
227        BrowserFrame.sJavaBridge.resume();
228    }
229
230    public WebSettings getSettings() {
231        return mSettings;
232    }
233
234    /**
235     * Add an error message to the client's console.
236     * @param message The message to add
237     * @param lineNumber the line on which the error occurred
238     * @param sourceID the filename of the source that caused the error.
239     */
240    protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
241        mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
242    }
243
244    /**
245     * Invoke a javascript alert.
246     * @param message The message displayed in the alert.
247     */
248    protected void jsAlert(String url, String message) {
249        mCallbackProxy.onJsAlert(url, message);
250    }
251
252    /**
253     * Notify the browser that the origin has exceeded it's database quota.
254     * @param url The URL that caused the overflow.
255     * @param databaseIdentifier The identifier of the database.
256     * @param currentQuota The current quota for the origin.
257     */
258    protected void exceededDatabaseQuota(String url,
259                                         String databaseIdentifier,
260                                         long currentQuota) {
261        // Inform the callback proxy of the quota overflow. Send an object
262        // that encapsulates a call to the nativeSetDatabaseQuota method to
263        // awaken the sleeping webcore thread when a decision from the
264        // client to allow or deny quota is available.
265        mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
266                currentQuota, getUsedQuota(), new WebStorage.QuotaUpdater() {
267                                  public void updateQuota(long quota) {
268                                      nativeSetNewStorageLimit(quota);
269                                  }
270                              });
271    }
272
273    /**
274     * Notify the browser that the appcache has exceeded its max size.
275     * @param spaceNeeded is the amount of disk space that would be needed
276     * in order for the last appcache operation to succeed.
277     */
278    protected void reachedMaxAppCacheSize(long spaceNeeded) {
279        mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(),
280                new WebStorage.QuotaUpdater() {
281                    public void updateQuota(long quota) {
282                        nativeSetNewStorageLimit(quota);
283                    }
284                });
285    }
286
287    /**
288     * Invoke a javascript confirm dialog.
289     * @param message The message displayed in the dialog.
290     * @return True if the user confirmed or false if the user cancelled.
291     */
292    protected boolean jsConfirm(String url, String message) {
293        return mCallbackProxy.onJsConfirm(url, message);
294    }
295
296    /**
297     * Invoke a javascript prompt dialog.
298     * @param message The message to be displayed in the dialog.
299     * @param defaultValue The default value in the prompt input.
300     * @return The input from the user or null to indicate the user cancelled
301     *         the dialog.
302     */
303    protected String jsPrompt(String url, String message, String defaultValue) {
304        return mCallbackProxy.onJsPrompt(url, message, defaultValue);
305    }
306
307    /**
308     * Invoke a javascript before unload dialog.
309     * @param url The url that is requesting the dialog.
310     * @param message The message displayed in the dialog.
311     * @return True if the user confirmed or false if the user cancelled. False
312     *         will cancel the navigation.
313     */
314    protected boolean jsUnload(String url, String message) {
315        return mCallbackProxy.onJsBeforeUnload(url, message);
316    }
317
318    /**
319     *
320     * Callback to notify that a JavaScript execution timeout has occured.
321     * @return True if the JavaScript execution should be interrupted. False
322     *         will continue the execution.
323     */
324    protected boolean jsInterrupt() {
325        return mCallbackProxy.onJsTimeout();
326    }
327
328    //-------------------------------------------------------------------------
329    // JNI methods
330    //-------------------------------------------------------------------------
331
332    static native String nativeFindAddress(String addr, boolean caseInsensitive);
333
334    /**
335     * Empty the picture set.
336     */
337    private native void nativeClearContent();
338
339    /**
340     * Create a flat picture from the set of pictures.
341     */
342    private native void nativeCopyContentToPicture(Picture picture);
343
344    /**
345     * Draw the picture set with a background color. Returns true
346     * if some individual picture took too long to draw and can be
347     * split into parts. Called from the UI thread.
348     */
349    private native boolean nativeDrawContent(Canvas canvas, int color);
350
351    /**
352     * check to see if picture is blank and in progress
353     */
354    private native boolean nativePictureReady();
355
356    /**
357     * Redraw a portion of the picture set. The Point wh returns the
358     * width and height of the overall picture.
359     */
360    private native boolean nativeRecordContent(Region invalRegion, Point wh);
361
362    /**
363     * Splits slow parts of the picture set. Called from the webkit
364     * thread after nativeDrawContent returns true.
365     */
366    private native void nativeSplitContent();
367
368    private native boolean nativeKey(int keyCode, int unichar,
369            int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
370            boolean isDown);
371
372    private native void nativeClick(int framePtr, int nodePtr);
373
374    private native void nativeSendListBoxChoices(boolean[] choices, int size);
375
376    private native void nativeSendListBoxChoice(int choice);
377
378    /*  Tell webkit what its width and height are, for the purposes
379        of layout/line-breaking. These coordinates are in document space,
380        which is the same as View coords unless we have zoomed the document
381        (see nativeSetZoom).
382        screenWidth is used by layout to wrap column around. If viewport uses
383        fixed size, screenWidth can be different from width with zooming.
384        should this be called nativeSetViewPortSize?
385    */
386    private native void nativeSetSize(int width, int height, int screenWidth,
387            float scale, int realScreenWidth, int screenHeight);
388
389    private native int nativeGetContentMinPrefWidth();
390
391    // Start: functions that deal with text editing
392    private native void nativeReplaceTextfieldText(
393            int oldStart, int oldEnd, String replace, int newStart, int newEnd,
394            int textGeneration);
395
396    private native void passToJs(int gen,
397            String currentText, int keyCode, int keyValue, boolean down,
398            boolean cap, boolean fn, boolean sym);
399
400    private native void nativeSetFocusControllerActive(boolean active);
401
402    private native void nativeSaveDocumentState(int frame);
403
404    private native void nativeMoveMouse(int framePtr, int x, int y);
405
406    private native void nativeMoveMouseIfLatest(int moveGeneration,
407            int framePtr, int x, int y);
408
409    private native String nativeRetrieveHref(int framePtr, int nodePtr);
410
411    private native void nativeTouchUp(int touchGeneration,
412            int framePtr, int nodePtr, int x, int y,
413            int size);
414
415    private native boolean nativeHandleTouchEvent(int action, int x, int y);
416
417    private native void nativeUpdateFrameCache();
418
419    private native void nativeSetSnapAnchor(int x, int y);
420
421    private native void nativeSnapToAnchor();
422
423    private native void nativeSetBackgroundColor(int color);
424
425    private native void nativeDumpDomTree(boolean useFile);
426
427    private native void nativeDumpRenderTree(boolean useFile);
428
429    private native void nativeDumpNavTree();
430
431    private native void nativeSetJsFlags(String flags);
432
433    /**
434     *  Delete text from start to end in the focused textfield. If there is no
435     *  focus, or if start == end, silently fail.  If start and end are out of
436     *  order, swap them.
437     *  @param  start   Beginning of selection to delete.
438     *  @param  end     End of selection to delete.
439     *  @param  textGeneration Text generation number when delete was pressed.
440     */
441    private native void nativeDeleteSelection(int start, int end,
442            int textGeneration);
443
444    /**
445     *  Set the selection to (start, end) in the focused textfield. If start and
446     *  end are out of order, swap them.
447     *  @param  start   Beginning of selection.
448     *  @param  end     End of selection.
449     */
450    private native void nativeSetSelection(int start, int end);
451
452    private native String nativeGetSelection(Region sel);
453
454    // Register a scheme to be treated as local scheme so that it can access
455    // local asset files for resources
456    private native void nativeRegisterURLSchemeAsLocal(String scheme);
457
458    /*
459     * Inform webcore that the user has decided whether to allow or deny new
460     * quota for the current origin or more space for the app cache, and that
461     * the main thread should wake up now.
462     * @param limit Is the new quota for an origin or new app cache max size.
463     */
464    private native void nativeSetNewStorageLimit(long limit);
465
466    private native void nativeUpdatePluginState(int framePtr, int nodePtr, int state);
467
468    // EventHub for processing messages
469    private final EventHub mEventHub;
470    // WebCore thread handler
471    private static Handler sWebCoreHandler;
472    // Class for providing Handler creation inside the WebCore thread.
473    private static class WebCoreThread implements Runnable {
474        // Message id for initializing a new WebViewCore.
475        private static final int INITIALIZE = 0;
476        private static final int REDUCE_PRIORITY = 1;
477        private static final int RESUME_PRIORITY = 2;
478        private static final int CACHE_TICKER = 3;
479        private static final int BLOCK_CACHE_TICKER = 4;
480        private static final int RESUME_CACHE_TICKER = 5;
481
482        private static final int CACHE_TICKER_INTERVAL = 60 * 1000; // 1 minute
483
484        private static boolean mCacheTickersBlocked = true;
485
486        public void run() {
487            Looper.prepare();
488            Assert.assertNull(sWebCoreHandler);
489            synchronized (WebViewCore.class) {
490                sWebCoreHandler = new Handler() {
491                    @Override
492                    public void handleMessage(Message msg) {
493                        switch (msg.what) {
494                            case INITIALIZE:
495                                WebViewCore core = (WebViewCore) msg.obj;
496                                core.initialize();
497                                break;
498
499                            case REDUCE_PRIORITY:
500                                // 3 is an adjustable number.
501                                Process.setThreadPriority(
502                                        Process.THREAD_PRIORITY_DEFAULT + 3 *
503                                        Process.THREAD_PRIORITY_LESS_FAVORABLE);
504                                break;
505
506                            case RESUME_PRIORITY:
507                                Process.setThreadPriority(
508                                        Process.THREAD_PRIORITY_DEFAULT);
509                                break;
510
511                            case CACHE_TICKER:
512                                if (!mCacheTickersBlocked) {
513                                    CacheManager.endCacheTransaction();
514                                    CacheManager.startCacheTransaction();
515                                    sendMessageDelayed(
516                                            obtainMessage(CACHE_TICKER),
517                                            CACHE_TICKER_INTERVAL);
518                                }
519                                break;
520
521                            case BLOCK_CACHE_TICKER:
522                                if (CacheManager.endCacheTransaction()) {
523                                    mCacheTickersBlocked = true;
524                                }
525                                break;
526
527                            case RESUME_CACHE_TICKER:
528                                if (CacheManager.startCacheTransaction()) {
529                                    mCacheTickersBlocked = false;
530                                }
531                                break;
532                        }
533                    }
534                };
535                WebViewCore.class.notify();
536            }
537            Looper.loop();
538        }
539    }
540
541    static class BaseUrlData {
542        String mBaseUrl;
543        String mData;
544        String mMimeType;
545        String mEncoding;
546        String mFailUrl;
547    }
548
549    static class CursorData {
550        CursorData() {}
551        CursorData(int frame, int node, int x, int y) {
552            mFrame = frame;
553            mX = x;
554            mY = y;
555        }
556        int mMoveGeneration;
557        int mFrame;
558        int mX;
559        int mY;
560    }
561
562    static class JSInterfaceData {
563        Object mObject;
564        String mInterfaceName;
565    }
566
567    static class JSKeyData {
568        String mCurrentText;
569        KeyEvent mEvent;
570    }
571
572    static class PostUrlData {
573        String mUrl;
574        byte[] mPostData;
575    }
576
577    static class DeleteSelectionData {
578        int mStart;
579        int mEnd;
580        int mTextGeneration;
581    }
582
583    static class ReplaceTextData {
584        String mReplace;
585        int mNewStart;
586        int mNewEnd;
587        int mTextGeneration;
588    }
589
590    static class TouchUpData {
591        int mMoveGeneration;
592        int mFrame;
593        int mNode;
594        int mX;
595        int mY;
596        int mSize;
597    }
598
599    static class TouchEventData {
600        int mAction;    // MotionEvent.getAction()
601        int mX;
602        int mY;
603    }
604
605    static class PluginStateData {
606        int mFrame;
607        int mNode;
608        int mState;
609    }
610
611        static final String[] HandlerDebugString = {
612            "SCROLL_TEXT_INPUT", // = 99
613            "LOAD_URL", // = 100;
614            "STOP_LOADING", // = 101;
615            "RELOAD", // = 102;
616            "KEY_DOWN", // = 103;
617            "KEY_UP", // = 104;
618            "VIEW_SIZE_CHANGED", // = 105;
619            "GO_BACK_FORWARD", // = 106;
620            "SET_SCROLL_OFFSET", // = 107;
621            "RESTORE_STATE", // = 108;
622            "PAUSE_TIMERS", // = 109;
623            "RESUME_TIMERS", // = 110;
624            "CLEAR_CACHE", // = 111;
625            "CLEAR_HISTORY", // = 112;
626            "SET_SELECTION", // = 113;
627            "REPLACE_TEXT", // = 114;
628            "PASS_TO_JS", // = 115;
629            "SET_GLOBAL_BOUNDS", // = 116;
630            "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117;
631            "CLICK", // = 118;
632            "SET_NETWORK_STATE", // = 119;
633            "DOC_HAS_IMAGES", // = 120;
634            "SET_SNAP_ANCHOR", // = 121;
635            "DELETE_SELECTION", // = 122;
636            "LISTBOX_CHOICES", // = 123;
637            "SINGLE_LISTBOX_CHOICE", // = 124;
638            "MESSAGE_RELAY", // = 125;
639            "SET_BACKGROUND_COLOR", // = 126;
640            "PLUGIN_STATE", // = 127;
641            "SAVE_DOCUMENT_STATE", // = 128;
642            "GET_SELECTION", // = 129;
643            "WEBKIT_DRAW", // = 130;
644            "SYNC_SCROLL", // = 131;
645            "POST_URL", // = 132;
646            "SPLIT_PICTURE_SET", // = 133;
647            "CLEAR_CONTENT", // = 134;
648            "SET_MOVE_MOUSE", // = 135;
649            "SET_MOVE_MOUSE_IF_LATEST", // = 136;
650            "REQUEST_CURSOR_HREF", // = 137;
651            "ADD_JS_INTERFACE", // = 138;
652            "LOAD_DATA", // = 139;
653            "TOUCH_UP", // = 140;
654            "TOUCH_EVENT", // = 141;
655            "SET_ACTIVE", // = 142;
656            "ON_PAUSE",     // = 143
657            "ON_RESUME",    // = 144
658            "FREE_MEMORY",  // = 145
659        };
660
661    class EventHub {
662        // Message Ids
663        static final int SCROLL_TEXT_INPUT = 99;
664        static final int LOAD_URL = 100;
665        static final int STOP_LOADING = 101;
666        static final int RELOAD = 102;
667        static final int KEY_DOWN = 103;
668        static final int KEY_UP = 104;
669        static final int VIEW_SIZE_CHANGED = 105;
670        static final int GO_BACK_FORWARD = 106;
671        static final int SET_SCROLL_OFFSET = 107;
672        static final int RESTORE_STATE = 108;
673        static final int PAUSE_TIMERS = 109;
674        static final int RESUME_TIMERS = 110;
675        static final int CLEAR_CACHE = 111;
676        static final int CLEAR_HISTORY = 112;
677        static final int SET_SELECTION = 113;
678        static final int REPLACE_TEXT = 114;
679        static final int PASS_TO_JS = 115;
680        static final int SET_GLOBAL_BOUNDS = 116;
681        static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117;
682        static final int CLICK = 118;
683        static final int SET_NETWORK_STATE = 119;
684        static final int DOC_HAS_IMAGES = 120;
685        static final int SET_SNAP_ANCHOR = 121;
686        static final int DELETE_SELECTION = 122;
687        static final int LISTBOX_CHOICES = 123;
688        static final int SINGLE_LISTBOX_CHOICE = 124;
689        static final int MESSAGE_RELAY = 125;
690        static final int SET_BACKGROUND_COLOR = 126;
691        static final int PLUGIN_STATE = 127; // plugin notifications
692        static final int SAVE_DOCUMENT_STATE = 128;
693        static final int GET_SELECTION = 129;
694        static final int WEBKIT_DRAW = 130;
695        static final int SYNC_SCROLL = 131;
696        static final int POST_URL = 132;
697        static final int SPLIT_PICTURE_SET = 133;
698        static final int CLEAR_CONTENT = 134;
699
700        // UI nav messages
701        static final int SET_MOVE_MOUSE = 135;
702        static final int SET_MOVE_MOUSE_IF_LATEST = 136;
703        static final int REQUEST_CURSOR_HREF = 137;
704        static final int ADD_JS_INTERFACE = 138;
705        static final int LOAD_DATA = 139;
706
707        // motion
708        static final int TOUCH_UP = 140;
709        // message used to pass UI touch events to WebCore
710        static final int TOUCH_EVENT = 141;
711
712        // Used to tell the focus controller not to draw the blinking cursor,
713        // based on whether the WebView has focus and whether the WebView's
714        // cursor matches the webpage's focus.
715        static final int SET_ACTIVE = 142;
716
717        // lifecycle activities for just this DOM (unlike pauseTimers, which
718        // is global)
719        static final int ON_PAUSE = 143;
720        static final int ON_RESUME = 144;
721        static final int FREE_MEMORY = 145;
722
723        // Network-based messaging
724        static final int CLEAR_SSL_PREF_TABLE = 150;
725
726        // Test harness messages
727        static final int REQUEST_EXT_REPRESENTATION = 160;
728        static final int REQUEST_DOC_AS_TEXT = 161;
729
730        // debugging
731        static final int DUMP_DOMTREE = 170;
732        static final int DUMP_RENDERTREE = 171;
733        static final int DUMP_NAVTREE = 172;
734
735        static final int SET_JS_FLAGS = 173;
736
737        // private message ids
738        private static final int DESTROY =     200;
739
740        // Private handler for WebCore messages.
741        private Handler mHandler;
742        // Message queue for containing messages before the WebCore thread is
743        // ready.
744        private ArrayList<Message> mMessages = new ArrayList<Message>();
745        // Flag for blocking messages. This is used during DESTROY to avoid
746        // posting more messages to the EventHub or to WebView's event handler.
747        private boolean mBlockMessages;
748
749        private int mTid;
750        private int mSavedPriority;
751
752        /**
753         * Prevent other classes from creating an EventHub.
754         */
755        private EventHub() {}
756
757        /**
758         * Transfer all messages to the newly created webcore thread handler.
759         */
760        private void transferMessages() {
761            mTid = Process.myTid();
762            mSavedPriority = Process.getThreadPriority(mTid);
763
764            mHandler = new Handler() {
765                @Override
766                public void handleMessage(Message msg) {
767                    if (DebugFlags.WEB_VIEW_CORE) {
768                        Log.v(LOGTAG, (msg.what < SCROLL_TEXT_INPUT || msg.what
769                                > FREE_MEMORY ? Integer.toString(msg.what)
770                                : HandlerDebugString[msg.what
771                                        - SCROLL_TEXT_INPUT])
772                                + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
773                                + " obj=" + msg.obj);
774                    }
775                    switch (msg.what) {
776                        case WEBKIT_DRAW:
777                            webkitDraw();
778                            break;
779
780                        case DESTROY:
781                            // Time to take down the world. Cancel all pending
782                            // loads and destroy the native view and frame.
783                            mBrowserFrame.destroy();
784                            mBrowserFrame = null;
785                            mNativeClass = 0;
786                            break;
787
788                        case SCROLL_TEXT_INPUT:
789                            nativeScrollFocusedTextInput(msg.arg1, msg.arg2);
790                            break;
791
792                        case LOAD_URL:
793                            loadUrl((String) msg.obj);
794                            break;
795
796                        case POST_URL: {
797                            PostUrlData param = (PostUrlData) msg.obj;
798                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
799                            break;
800                        }
801                        case LOAD_DATA:
802                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
803                            String baseUrl = loadParams.mBaseUrl;
804                            if (baseUrl != null) {
805                                int i = baseUrl.indexOf(':');
806                                if (i > 0) {
807                                    /*
808                                     * In 1.0, {@link
809                                     * WebView#loadDataWithBaseURL} can access
810                                     * local asset files as long as the data is
811                                     * valid. In the new WebKit, the restriction
812                                     * is tightened. To be compatible with 1.0,
813                                     * we automatically add the scheme of the
814                                     * baseUrl for local access as long as it is
815                                     * not http(s)/ftp(s)/about/javascript
816                                     */
817                                    String scheme = baseUrl.substring(0, i);
818                                    if (!scheme.startsWith("http") &&
819                                            !scheme.startsWith("ftp") &&
820                                            !scheme.startsWith("about") &&
821                                            !scheme.startsWith("javascript")) {
822                                        nativeRegisterURLSchemeAsLocal(scheme);
823                                    }
824                                }
825                            }
826                            mBrowserFrame.loadData(baseUrl,
827                                    loadParams.mData,
828                                    loadParams.mMimeType,
829                                    loadParams.mEncoding,
830                                    loadParams.mFailUrl);
831                            break;
832
833                        case STOP_LOADING:
834                            // If the WebCore has committed the load, but not
835                            // finished the first layout yet, we need to set
836                            // first layout done to trigger the interpreted side sync
837                            // up with native side
838                            if (mBrowserFrame.committed()
839                                    && !mBrowserFrame.firstLayoutDone()) {
840                                mBrowserFrame.didFirstLayout();
841                            }
842                            // Do this after syncing up the layout state.
843                            stopLoading();
844                            break;
845
846                        case RELOAD:
847                            mBrowserFrame.reload(false);
848                            break;
849
850                        case KEY_DOWN:
851                            key((KeyEvent) msg.obj, true);
852                            break;
853
854                        case KEY_UP:
855                            key((KeyEvent) msg.obj, false);
856                            break;
857
858                        case CLICK:
859                            nativeClick(msg.arg1, msg.arg2);
860                            break;
861
862                        case VIEW_SIZE_CHANGED:
863                            viewSizeChanged(msg.arg1, msg.arg2,
864                                    ((Float) msg.obj).floatValue());
865                            break;
866
867                        case SET_SCROLL_OFFSET:
868                            // note: these are in document coordinates
869                            // (inv-zoom)
870                            Point pt = (Point) msg.obj;
871                            nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
872                            break;
873
874                        case SET_GLOBAL_BOUNDS:
875                            Rect r = (Rect) msg.obj;
876                            nativeSetGlobalBounds(r.left, r.top, r.width(),
877                                r.height());
878                            break;
879
880                        case GO_BACK_FORWARD:
881                            // If it is a standard load and the load is not
882                            // committed yet, we interpret BACK as RELOAD
883                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
884                                    (mBrowserFrame.loadType() ==
885                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
886                                mBrowserFrame.reload(true);
887                            } else {
888                                mBrowserFrame.goBackOrForward(msg.arg1);
889                            }
890                            break;
891
892                        case RESTORE_STATE:
893                            stopLoading();
894                            restoreState(msg.arg1);
895                            break;
896
897                        case PAUSE_TIMERS:
898                            mSavedPriority = Process.getThreadPriority(mTid);
899                            Process.setThreadPriority(mTid,
900                                    Process.THREAD_PRIORITY_BACKGROUND);
901                            pauseTimers();
902                            if (CacheManager.disableTransaction()) {
903                                WebCoreThread.mCacheTickersBlocked = true;
904                                sWebCoreHandler.removeMessages(
905                                        WebCoreThread.CACHE_TICKER);
906                            }
907                            break;
908
909                        case RESUME_TIMERS:
910                            Process.setThreadPriority(mTid, mSavedPriority);
911                            resumeTimers();
912                            if (CacheManager.enableTransaction()) {
913                                WebCoreThread.mCacheTickersBlocked = false;
914                                sWebCoreHandler.sendMessageDelayed(
915                                        sWebCoreHandler.obtainMessage(
916                                        WebCoreThread.CACHE_TICKER),
917                                        WebCoreThread.CACHE_TICKER_INTERVAL);
918                            }
919                            break;
920
921                        case ON_PAUSE:
922                            nativePause();
923                            break;
924
925                        case ON_RESUME:
926                            nativeResume();
927                            break;
928
929                        case FREE_MEMORY:
930                            clearCache(false);
931                            nativeFreeMemory();
932                            break;
933
934                        case PLUGIN_STATE:
935                            PluginStateData psd = (PluginStateData) msg.obj;
936                            nativeUpdatePluginState(psd.mFrame, psd.mNode, psd.mState);
937                            break;
938
939                        case SET_NETWORK_STATE:
940                            if (BrowserFrame.sJavaBridge == null) {
941                                throw new IllegalStateException("No WebView " +
942                                        "has been created in this process!");
943                            }
944                            BrowserFrame.sJavaBridge
945                                    .setNetworkOnLine(msg.arg1 == 1);
946                            break;
947
948                        case CLEAR_CACHE:
949                            clearCache(msg.arg1 == 1);
950                            break;
951
952                        case CLEAR_HISTORY:
953                            mCallbackProxy.getBackForwardList().
954                                    close(mBrowserFrame.mNativeFrame);
955                            break;
956
957                        case REPLACE_TEXT:
958                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
959                            nativeReplaceTextfieldText(msg.arg1, msg.arg2,
960                                    rep.mReplace, rep.mNewStart, rep.mNewEnd,
961                                    rep.mTextGeneration);
962                            break;
963
964                        case PASS_TO_JS: {
965                            JSKeyData jsData = (JSKeyData) msg.obj;
966                            KeyEvent evt = jsData.mEvent;
967                            int keyCode = evt.getKeyCode();
968                            int keyValue = evt.getUnicodeChar();
969                            int generation = msg.arg1;
970                            passToJs(generation,
971                                    jsData.mCurrentText,
972                                    keyCode,
973                                    keyValue,
974                                    evt.isDown(),
975                                    evt.isShiftPressed(), evt.isAltPressed(),
976                                    evt.isSymPressed());
977                            break;
978                        }
979
980                        case SAVE_DOCUMENT_STATE: {
981                            CursorData cDat = (CursorData) msg.obj;
982                            nativeSaveDocumentState(cDat.mFrame);
983                            break;
984                        }
985
986                        case CLEAR_SSL_PREF_TABLE:
987                            Network.getInstance(mContext)
988                                    .clearUserSslPrefTable();
989                            break;
990
991                        case TOUCH_UP:
992                            TouchUpData touchUpData = (TouchUpData) msg.obj;
993                            nativeTouchUp(touchUpData.mMoveGeneration,
994                                    touchUpData.mFrame, touchUpData.mNode,
995                                    touchUpData.mX, touchUpData.mY,
996                                    touchUpData.mSize);
997                            break;
998
999                        case TOUCH_EVENT: {
1000                            TouchEventData ted = (TouchEventData) msg.obj;
1001                            Message.obtain(
1002                                    mWebView.mPrivateHandler,
1003                                    WebView.PREVENT_TOUCH_ID, ted.mAction,
1004                                    nativeHandleTouchEvent(ted.mAction, ted.mX,
1005                                            ted.mY) ? 1 : 0).sendToTarget();
1006                            break;
1007                        }
1008
1009                        case SET_ACTIVE:
1010                            nativeSetFocusControllerActive(msg.arg1 == 1);
1011                            break;
1012
1013                        case ADD_JS_INTERFACE:
1014                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
1015                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
1016                                    jsData.mInterfaceName);
1017                            break;
1018
1019                        case REQUEST_EXT_REPRESENTATION:
1020                            mBrowserFrame.externalRepresentation(
1021                                    (Message) msg.obj);
1022                            break;
1023
1024                        case REQUEST_DOC_AS_TEXT:
1025                            mBrowserFrame.documentAsText((Message) msg.obj);
1026                            break;
1027
1028                        case SET_MOVE_MOUSE:
1029                            CursorData cursorData = (CursorData) msg.obj;
1030                            nativeMoveMouse(cursorData.mFrame,
1031                                     cursorData.mX, cursorData.mY);
1032                            break;
1033
1034                        case SET_MOVE_MOUSE_IF_LATEST:
1035                            CursorData cData = (CursorData) msg.obj;
1036                            nativeMoveMouseIfLatest(cData.mMoveGeneration,
1037                                    cData.mFrame,
1038                                    cData.mX, cData.mY);
1039                            break;
1040
1041                        case REQUEST_CURSOR_HREF: {
1042                            Message hrefMsg = (Message) msg.obj;
1043                            String res = nativeRetrieveHref(msg.arg1, msg.arg2);
1044                            hrefMsg.getData().putString("url", res);
1045                            hrefMsg.sendToTarget();
1046                            break;
1047                        }
1048
1049                        case UPDATE_CACHE_AND_TEXT_ENTRY:
1050                            nativeUpdateFrameCache();
1051                            // FIXME: this should provide a minimal rectangle
1052                            if (mWebView != null) {
1053                                mWebView.postInvalidate();
1054                            }
1055                            sendUpdateTextEntry();
1056                            break;
1057
1058                        case DOC_HAS_IMAGES:
1059                            Message imageResult = (Message) msg.obj;
1060                            imageResult.arg1 =
1061                                    mBrowserFrame.documentHasImages() ? 1 : 0;
1062                            imageResult.sendToTarget();
1063                            break;
1064
1065                        case SET_SNAP_ANCHOR:
1066                            nativeSetSnapAnchor(msg.arg1, msg.arg2);
1067                            break;
1068
1069                        case DELETE_SELECTION:
1070                            DeleteSelectionData deleteSelectionData
1071                                    = (DeleteSelectionData) msg.obj;
1072                            nativeDeleteSelection(deleteSelectionData.mStart,
1073                                    deleteSelectionData.mEnd,
1074                                    deleteSelectionData.mTextGeneration);
1075                            break;
1076
1077                        case SET_SELECTION:
1078                            nativeSetSelection(msg.arg1, msg.arg2);
1079                            break;
1080
1081                        case LISTBOX_CHOICES:
1082                            SparseBooleanArray choices = (SparseBooleanArray)
1083                                    msg.obj;
1084                            int choicesSize = msg.arg1;
1085                            boolean[] choicesArray = new boolean[choicesSize];
1086                            for (int c = 0; c < choicesSize; c++) {
1087                                choicesArray[c] = choices.get(c);
1088                            }
1089                            nativeSendListBoxChoices(choicesArray,
1090                                    choicesSize);
1091                            break;
1092
1093                        case SINGLE_LISTBOX_CHOICE:
1094                            nativeSendListBoxChoice(msg.arg1);
1095                            break;
1096
1097                        case SET_BACKGROUND_COLOR:
1098                            nativeSetBackgroundColor(msg.arg1);
1099                            break;
1100
1101                        case GET_SELECTION:
1102                            String str = nativeGetSelection((Region) msg.obj);
1103                            Message.obtain(mWebView.mPrivateHandler
1104                                    , WebView.UPDATE_CLIPBOARD, str)
1105                                    .sendToTarget();
1106                            break;
1107
1108                        case DUMP_DOMTREE:
1109                            nativeDumpDomTree(msg.arg1 == 1);
1110                            break;
1111
1112                        case DUMP_RENDERTREE:
1113                            nativeDumpRenderTree(msg.arg1 == 1);
1114                            break;
1115
1116                        case DUMP_NAVTREE:
1117                            nativeDumpNavTree();
1118                            break;
1119
1120                        case SET_JS_FLAGS:
1121                            nativeSetJsFlags((String)msg.obj);
1122                            break;
1123
1124                        case SYNC_SCROLL:
1125                            mWebkitScrollX = msg.arg1;
1126                            mWebkitScrollY = msg.arg2;
1127                            break;
1128
1129                        case SPLIT_PICTURE_SET:
1130                            nativeSplitContent();
1131                            mSplitPictureIsScheduled = false;
1132                            break;
1133
1134                        case CLEAR_CONTENT:
1135                            // Clear the view so that onDraw() will draw nothing
1136                            // but white background
1137                            // (See public method WebView.clearView)
1138                            nativeClearContent();
1139                            break;
1140
1141                        case MESSAGE_RELAY:
1142                            if (msg.obj instanceof Message) {
1143                                ((Message) msg.obj).sendToTarget();
1144                            }
1145                            break;
1146                    }
1147                }
1148            };
1149            // Take all queued messages and resend them to the new handler.
1150            synchronized (this) {
1151                int size = mMessages.size();
1152                for (int i = 0; i < size; i++) {
1153                    mHandler.sendMessage(mMessages.get(i));
1154                }
1155                mMessages = null;
1156            }
1157        }
1158
1159        /**
1160         * Send a message internally to the queue or to the handler
1161         */
1162        private synchronized void sendMessage(Message msg) {
1163            if (mBlockMessages) {
1164                return;
1165            }
1166            if (mMessages != null) {
1167                mMessages.add(msg);
1168            } else {
1169                mHandler.sendMessage(msg);
1170            }
1171        }
1172
1173        private synchronized void removeMessages(int what) {
1174            if (mBlockMessages) {
1175                return;
1176            }
1177            if (what == EventHub.WEBKIT_DRAW) {
1178                mDrawIsScheduled = false;
1179            }
1180            if (mMessages != null) {
1181                Log.w(LOGTAG, "Not supported in this case.");
1182            } else {
1183                mHandler.removeMessages(what);
1184            }
1185        }
1186
1187        private synchronized void sendMessageDelayed(Message msg, long delay) {
1188            if (mBlockMessages) {
1189                return;
1190            }
1191            mHandler.sendMessageDelayed(msg, delay);
1192        }
1193
1194        /**
1195         * Send a message internally to the front of the queue.
1196         */
1197        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1198            if (mBlockMessages) {
1199                return;
1200            }
1201            if (mMessages != null) {
1202                mMessages.add(0, msg);
1203            } else {
1204                mHandler.sendMessageAtFrontOfQueue(msg);
1205            }
1206        }
1207
1208        /**
1209         * Remove all the messages.
1210         */
1211        private synchronized void removeMessages() {
1212            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1213            mDrawIsScheduled = false;
1214            mSplitPictureIsScheduled = false;
1215            if (mMessages != null) {
1216                mMessages.clear();
1217            } else {
1218                mHandler.removeCallbacksAndMessages(null);
1219            }
1220        }
1221
1222        /**
1223         * Block sending messages to the EventHub.
1224         */
1225        private synchronized void blockMessages() {
1226            mBlockMessages = true;
1227        }
1228    }
1229
1230    //-------------------------------------------------------------------------
1231    // Methods called by host activity (in the same thread)
1232    //-------------------------------------------------------------------------
1233
1234    void stopLoading() {
1235        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1236        if (mBrowserFrame != null) {
1237            mBrowserFrame.stopLoading();
1238        }
1239    }
1240
1241    //-------------------------------------------------------------------------
1242    // Methods called by WebView
1243    // If it refers to local variable, it needs synchronized().
1244    // If it needs WebCore, it has to send message.
1245    //-------------------------------------------------------------------------
1246
1247    void sendMessage(Message msg) {
1248        mEventHub.sendMessage(msg);
1249    }
1250
1251    void sendMessage(int what) {
1252        mEventHub.sendMessage(Message.obtain(null, what));
1253    }
1254
1255    void sendMessage(int what, Object obj) {
1256        mEventHub.sendMessage(Message.obtain(null, what, obj));
1257    }
1258
1259    void sendMessage(int what, int arg1) {
1260        // just ignore the second argument (make it 0)
1261        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1262    }
1263
1264    void sendMessage(int what, int arg1, int arg2) {
1265        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1266    }
1267
1268    void sendMessage(int what, int arg1, Object obj) {
1269        // just ignore the second argument (make it 0)
1270        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1271    }
1272
1273    void sendMessage(int what, int arg1, int arg2, Object obj) {
1274        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1275    }
1276
1277    void sendMessageDelayed(int what, Object obj, long delay) {
1278        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1279    }
1280
1281    void removeMessages(int what) {
1282        mEventHub.removeMessages(what);
1283    }
1284
1285    void removeMessages() {
1286        mEventHub.removeMessages();
1287    }
1288
1289    /**
1290     * Removes pending messages and trigger a DESTROY message to send to
1291     * WebCore.
1292     * Called from UI thread.
1293     */
1294    void destroy() {
1295        // We don't want anyone to post a message between removing pending
1296        // messages and sending the destroy message.
1297        synchronized (mEventHub) {
1298            mEventHub.removeMessages();
1299            mEventHub.sendMessageAtFrontOfQueue(
1300                    Message.obtain(null, EventHub.DESTROY));
1301            mEventHub.blockMessages();
1302            mWebView = null;
1303        }
1304    }
1305
1306    //-------------------------------------------------------------------------
1307    // WebViewCore private methods
1308    //-------------------------------------------------------------------------
1309
1310    private void clearCache(boolean includeDiskFiles) {
1311        mBrowserFrame.clearCache();
1312        if (includeDiskFiles) {
1313            CacheManager.removeAllCacheFiles();
1314        }
1315    }
1316
1317    private void loadUrl(String url) {
1318        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1319        mBrowserFrame.loadUrl(url);
1320    }
1321
1322    private void key(KeyEvent evt, boolean isDown) {
1323        if (DebugFlags.WEB_VIEW_CORE) {
1324            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1325                    + evt);
1326        }
1327        int keyCode = evt.getKeyCode();
1328        if (!nativeKey(keyCode, evt.getUnicodeChar(),
1329                evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
1330                evt.isSymPressed(),
1331                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1332            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1333                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1334                if (DebugFlags.WEB_VIEW_CORE) {
1335                    Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
1336                }
1337                if (mWebView != null && evt.isDown()) {
1338                    Message.obtain(mWebView.mPrivateHandler,
1339                            WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
1340                }
1341                return;
1342            }
1343            // bubble up the event handling
1344            // but do not bubble up the ENTER key, which would open the search
1345            // bar without any text.
1346            mCallbackProxy.onUnhandledKeyEvent(evt);
1347        }
1348    }
1349
1350    // These values are used to avoid requesting a layout based on old values
1351    private int mCurrentViewWidth = 0;
1352    private int mCurrentViewHeight = 0;
1353
1354    // notify webkit that our virtual view size changed size (after inv-zoom)
1355    private void viewSizeChanged(int w, int h, float scale) {
1356        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE onSizeChanged");
1357        if (w == 0) {
1358            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1359            return;
1360        }
1361        if (mSettings.getUseWideViewPort()) {
1362            int width;
1363            if (mViewportWidth == -1) {
1364                if (mSettings.getLayoutAlgorithm() ==
1365                        WebSettings.LayoutAlgorithm.NORMAL) {
1366                    width = WebView.ZOOM_OUT_WIDTH;
1367                } else {
1368                    /*
1369                     * if a page's minimum preferred width is wider than the
1370                     * given "w", use it instead to get better layout result. If
1371                     * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1372                     * wider. If we start a page with screen width, due to the
1373                     * delay between {@link #didFirstLayout} and
1374                     * {@link #viewSizeChanged},
1375                     * {@link #nativeGetContentMinPrefWidth} will return a more
1376                     * accurate value than initial 0 to result a better layout.
1377                     * In the worse case, the native width will be adjusted when
1378                     * next zoom or screen orientation change happens.
1379                     */
1380                    width = Math.max(w, nativeGetContentMinPrefWidth());
1381                }
1382            } else {
1383                width = Math.max(w, mViewportWidth);
1384            }
1385            // while in zoom overview mode, the text are wrapped to the screen
1386            // width matching mWebView.mLastScale. So that we don't trigger
1387            // re-flow while toggling between overview mode and normal mode.
1388            nativeSetSize(width, Math.round((float) width * h / w),
1389                    Math.round(mWebView.mInZoomOverview ? w * scale
1390                            / mWebView.mLastScale : w), scale, w, h);
1391        } else {
1392            nativeSetSize(w, h, w, scale, w, h);
1393        }
1394        // Remember the current width and height
1395        boolean needInvalidate = (mCurrentViewWidth == 0);
1396        mCurrentViewWidth = w;
1397        mCurrentViewHeight = h;
1398        if (needInvalidate) {
1399            // ensure {@link #webkitDraw} is called as we were blocking in
1400            // {@link #contentDraw} when mCurrentViewWidth is 0
1401            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1402            contentDraw();
1403        }
1404        mEventHub.sendMessage(Message.obtain(null,
1405                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1406    }
1407
1408    private void sendUpdateTextEntry() {
1409        if (mWebView != null) {
1410            Message.obtain(mWebView.mPrivateHandler,
1411                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1412        }
1413    }
1414
1415    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1416    // callbacks. Computes the sum of database quota for all origins.
1417    private long getUsedQuota() {
1418        WebStorage webStorage = WebStorage.getInstance();
1419        Set<String> origins = webStorage.getOrigins();
1420        if (origins == null) {
1421            return 0;
1422        }
1423        long usedQuota = 0;
1424        for (String origin : origins) {
1425            usedQuota += webStorage.getQuotaForOrigin(origin);
1426        }
1427        return usedQuota;
1428    }
1429
1430    // Used to avoid posting more than one draw message.
1431    private boolean mDrawIsScheduled;
1432
1433    // Used to avoid posting more than one split picture message.
1434    private boolean mSplitPictureIsScheduled;
1435
1436    // Used to suspend drawing.
1437    private boolean mDrawIsPaused;
1438
1439    // Used to end scale+scroll mode, accessed by both threads
1440    boolean mEndScaleZoom = false;
1441
1442    public class DrawData {
1443        public DrawData() {
1444            mInvalRegion = new Region();
1445            mWidthHeight = new Point();
1446        }
1447        public Region mInvalRegion;
1448        public Point mViewPoint;
1449        public Point mWidthHeight;
1450        public int mMinPrefWidth;
1451    }
1452
1453    private void webkitDraw() {
1454        mDrawIsScheduled = false;
1455        DrawData draw = new DrawData();
1456        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
1457        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
1458                == false) {
1459            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
1460            return;
1461        }
1462        if (mWebView != null) {
1463            // Send the native view size that was used during the most recent
1464            // layout.
1465            draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1466            if (WebView.ENABLE_DOUBLETAP_ZOOM && mSettings.getUseWideViewPort()) {
1467                draw.mMinPrefWidth = nativeGetContentMinPrefWidth();
1468            }
1469            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1470            Message.obtain(mWebView.mPrivateHandler,
1471                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
1472            if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1473                // as we have the new picture, try to sync the scroll position
1474                Message.obtain(mWebView.mPrivateHandler,
1475                        WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1476                        mWebkitScrollY).sendToTarget();
1477                mWebkitScrollX = mWebkitScrollY = 0;
1478            }
1479            // nativeSnapToAnchor() needs to be called after NEW_PICTURE_MSG_ID
1480            // is sent, so that scroll will be based on the new content size.
1481            nativeSnapToAnchor();
1482        }
1483    }
1484
1485    ///////////////////////////////////////////////////////////////////////////
1486    // These are called from the UI thread, not our thread
1487
1488    static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
1489                                         Paint.DITHER_FLAG |
1490                                         Paint.SUBPIXEL_TEXT_FLAG;
1491    static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
1492                                           Paint.DITHER_FLAG;
1493
1494    final DrawFilter mZoomFilter =
1495                    new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
1496    final DrawFilter mScrollFilter =
1497                    new PaintFlagsDrawFilter(SCROLL_BITS, 0);
1498
1499    /* package */ void drawContentPicture(Canvas canvas, int color,
1500                                          boolean animatingZoom,
1501                                          boolean animatingScroll) {
1502        DrawFilter df = null;
1503        if (animatingZoom) {
1504            df = mZoomFilter;
1505        } else if (animatingScroll) {
1506            df = mScrollFilter;
1507        }
1508        canvas.setDrawFilter(df);
1509        boolean tookTooLong = nativeDrawContent(canvas, color);
1510        canvas.setDrawFilter(null);
1511        if (tookTooLong && mSplitPictureIsScheduled == false) {
1512            mSplitPictureIsScheduled = true;
1513            sendMessage(EventHub.SPLIT_PICTURE_SET);
1514        }
1515    }
1516
1517    /* package */ boolean pictureReady() {
1518        return nativePictureReady();
1519    }
1520
1521    /*package*/ Picture copyContentPicture() {
1522        Picture result = new Picture();
1523        nativeCopyContentToPicture(result);
1524        return result;
1525    }
1526
1527    static void pauseUpdate(WebViewCore core) {
1528        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1529        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1530        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1531        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1532                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1533        // Note: there is one possible failure mode. If pauseUpdate() is called
1534        // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
1535        // of the queue and about to be executed. mDrawIsScheduled may be set to
1536        // false in webkitDraw(). So update won't be blocked. But at least the
1537        // webcore thread priority is still lowered.
1538        if (core != null) {
1539            synchronized (core) {
1540                core.mDrawIsPaused = true;
1541                core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1542            }
1543        }
1544    }
1545
1546    static void resumeUpdate(WebViewCore core) {
1547        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1548        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1549        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1550        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1551                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1552        if (core != null) {
1553            synchronized (core) {
1554                core.mDrawIsScheduled = false;
1555                core.mDrawIsPaused = false;
1556                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate");
1557                core.contentDraw();
1558            }
1559        }
1560    }
1561
1562    static void startCacheTransaction() {
1563        sWebCoreHandler.sendMessage(sWebCoreHandler
1564                .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
1565    }
1566
1567    static void endCacheTransaction() {
1568        sWebCoreHandler.sendMessage(sWebCoreHandler
1569                .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
1570    }
1571
1572    //////////////////////////////////////////////////////////////////////////
1573
1574    private void restoreState(int index) {
1575        WebBackForwardList list = mCallbackProxy.getBackForwardList();
1576        int size = list.getSize();
1577        for (int i = 0; i < size; i++) {
1578            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1579        }
1580        mBrowserFrame.mLoadInitFromJava = true;
1581        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1582        mBrowserFrame.mLoadInitFromJava = false;
1583    }
1584
1585    //-------------------------------------------------------------------------
1586    // Implement abstract methods in WebViewCore, native WebKit callback part
1587    //-------------------------------------------------------------------------
1588
1589    // called from JNI or WebView thread
1590    /* package */ void contentDraw() {
1591        // don't update the Picture until we have an initial width and finish
1592        // the first layout
1593        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1594            return;
1595        }
1596        // only fire an event if this is our first request
1597        synchronized (this) {
1598            if (mDrawIsPaused || mDrawIsScheduled) {
1599                return;
1600            }
1601            mDrawIsScheduled = true;
1602            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1603        }
1604    }
1605
1606    // called by JNI
1607    private void contentScrollBy(int dx, int dy, boolean animate) {
1608        if (!mBrowserFrame.firstLayoutDone()) {
1609            // Will this happen? If yes, we need to do something here.
1610            return;
1611        }
1612        if (mWebView != null) {
1613            Message msg = Message.obtain(mWebView.mPrivateHandler,
1614                    WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
1615            if (mDrawIsScheduled) {
1616                mEventHub.sendMessage(Message.obtain(null,
1617                        EventHub.MESSAGE_RELAY, msg));
1618            } else {
1619                msg.sendToTarget();
1620            }
1621        }
1622    }
1623
1624    // called by JNI
1625    private void contentScrollTo(int x, int y) {
1626        if (!mBrowserFrame.firstLayoutDone()) {
1627            /*
1628             * WebKit restore state will be called before didFirstLayout(),
1629             * remember the position as it has to be applied after restoring
1630             * zoom factor which is controlled by screenWidth.
1631             */
1632            mRestoredX = x;
1633            mRestoredY = y;
1634            return;
1635        }
1636        if (mWebView != null) {
1637            Message msg = Message.obtain(mWebView.mPrivateHandler,
1638                    WebView.SCROLL_TO_MSG_ID, x, y);
1639            if (mDrawIsScheduled) {
1640                mEventHub.sendMessage(Message.obtain(null,
1641                        EventHub.MESSAGE_RELAY, msg));
1642            } else {
1643                msg.sendToTarget();
1644            }
1645        }
1646    }
1647
1648    // called by JNI
1649    private void contentSpawnScrollTo(int x, int y) {
1650        if (!mBrowserFrame.firstLayoutDone()) {
1651            /*
1652             * WebKit restore state will be called before didFirstLayout(),
1653             * remember the position as it has to be applied after restoring
1654             * zoom factor which is controlled by screenWidth.
1655             */
1656            mRestoredX = x;
1657            mRestoredY = y;
1658            return;
1659        }
1660        if (mWebView != null) {
1661            Message msg = Message.obtain(mWebView.mPrivateHandler,
1662                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
1663            if (mDrawIsScheduled) {
1664                mEventHub.sendMessage(Message.obtain(null,
1665                        EventHub.MESSAGE_RELAY, msg));
1666            } else {
1667                msg.sendToTarget();
1668            }
1669        }
1670    }
1671
1672    // called by JNI
1673    private void sendNotifyProgressFinished() {
1674        sendUpdateTextEntry();
1675        // as CacheManager can behave based on database transaction, we need to
1676        // call tick() to trigger endTransaction
1677        sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
1678        sWebCoreHandler.sendMessage(sWebCoreHandler
1679                .obtainMessage(WebCoreThread.CACHE_TICKER));
1680        contentDraw();
1681    }
1682
1683    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
1684        be scaled before they can be used by the view system, which happens
1685        in WebView since it (and its thread) know the current scale factor.
1686     */
1687    private void sendViewInvalidate(int left, int top, int right, int bottom) {
1688        if (mWebView != null) {
1689            Message.obtain(mWebView.mPrivateHandler,
1690                           WebView.INVAL_RECT_MSG_ID,
1691                           new Rect(left, top, right, bottom)).sendToTarget();
1692        }
1693    }
1694
1695    /* package */ WebView getWebView() {
1696        return mWebView;
1697    }
1698
1699    private native void setViewportSettingsFromNative();
1700
1701    // called by JNI
1702    private void didFirstLayout(boolean standardLoad) {
1703        // Trick to ensure that the Picture has the exact height for the content
1704        // by forcing to layout with 0 height after the page is ready, which is
1705        // indicated by didFirstLayout. This is essential to get rid of the
1706        // white space in the GMail which uses WebView for message view.
1707        if (mWebView != null && mWebView.mHeightCanMeasure) {
1708            mWebView.mLastHeightSent = 0;
1709            // Send a negative scale to indicate that WebCore should reuse the
1710            // current scale
1711            mEventHub.sendMessage(Message.obtain(null,
1712                    EventHub.VIEW_SIZE_CHANGED, mWebView.mLastWidthSent,
1713                    mWebView.mLastHeightSent, -1.0f));
1714        }
1715
1716        mBrowserFrame.didFirstLayout();
1717
1718        // reset the scroll position as it is a new page now
1719        mWebkitScrollX = mWebkitScrollY = 0;
1720
1721        // set the viewport settings from WebKit
1722        setViewportSettingsFromNative();
1723
1724        // adjust the default scale to match the density
1725        if (WebView.DEFAULT_SCALE_PERCENT != 100) {
1726            float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f;
1727            if (mViewportInitialScale > 0) {
1728                mViewportInitialScale *= adjust;
1729            }
1730            if (mViewportMinimumScale > 0) {
1731                mViewportMinimumScale *= adjust;
1732            }
1733            if (mViewportMaximumScale > 0) {
1734                mViewportMaximumScale *= adjust;
1735            }
1736        }
1737
1738        // infer the values if they are not defined.
1739        if (mViewportWidth == 0) {
1740            if (mViewportInitialScale == 0) {
1741                mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT;
1742            }
1743            if (mViewportMinimumScale == 0) {
1744                mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT;
1745            }
1746        }
1747        if (mViewportUserScalable == false) {
1748            mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT;
1749            mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT;
1750            mViewportMaximumScale = WebView.DEFAULT_SCALE_PERCENT;
1751        }
1752        if (mViewportMinimumScale > mViewportInitialScale) {
1753            if (mViewportInitialScale == 0) {
1754                mViewportInitialScale = mViewportMinimumScale;
1755            } else {
1756                mViewportMinimumScale = mViewportInitialScale;
1757            }
1758        }
1759        if (mViewportMaximumScale > 0) {
1760            if (mViewportMaximumScale < mViewportInitialScale) {
1761                mViewportMaximumScale = mViewportInitialScale;
1762            } else if (mViewportInitialScale == 0) {
1763                mViewportInitialScale = mViewportMaximumScale;
1764            }
1765        }
1766        if (mViewportWidth < 0
1767                && mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) {
1768            mViewportWidth = 0;
1769        }
1770
1771        // now notify webview
1772        if (mWebView != null) {
1773            WebView.ScaleLimitData scaleLimit = new WebView.ScaleLimitData();
1774            scaleLimit.mMinScale = mViewportMinimumScale;
1775            scaleLimit.mMaxScale = mViewportMaximumScale;
1776
1777            if (mRestoredScale > 0) {
1778                Message.obtain(mWebView.mPrivateHandler,
1779                        WebView.DID_FIRST_LAYOUT_MSG_ID,
1780                        mRestoredScreenWidthScale > 0 ?
1781                        -mRestoredScreenWidthScale : mRestoredScale, 0,
1782                        scaleLimit).sendToTarget();
1783            } else {
1784                // if standardLoad is true, use mViewportInitialScale, otherwise
1785                // pass -1 to the WebView to indicate no change of the scale.
1786                Message.obtain(mWebView.mPrivateHandler,
1787                        WebView.DID_FIRST_LAYOUT_MSG_ID,
1788                        standardLoad ? mViewportInitialScale : -1,
1789                        mViewportWidth, scaleLimit).sendToTarget();
1790            }
1791
1792            // force an early draw for quick feedback after the first layout
1793            if (mCurrentViewWidth != 0) {
1794                synchronized (this) {
1795                    if (mDrawIsScheduled) {
1796                        mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1797                    }
1798                    mDrawIsScheduled = true;
1799                    // if no restored offset, move the new page to (0, 0)
1800                    mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
1801                            EventHub.MESSAGE_RELAY, Message.obtain(
1802                                    mWebView.mPrivateHandler,
1803                                    WebView.SCROLL_TO_MSG_ID, mRestoredX,
1804                                    mRestoredY)));
1805                    mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
1806                            EventHub.WEBKIT_DRAW));
1807                }
1808            }
1809
1810            // reset restored offset, scale
1811            mRestoredX = mRestoredY = mRestoredScale = mRestoredScreenWidthScale = 0;
1812        }
1813    }
1814
1815    // called by JNI
1816    private void restoreScale(int scale) {
1817        if (mBrowserFrame.firstLayoutDone() == false) {
1818            mRestoredScale = scale;
1819        }
1820    }
1821
1822    // called by JNI
1823    private void restoreScreenWidthScale(int scale) {
1824        if (!WebView.ENABLE_DOUBLETAP_ZOOM || !mSettings.getUseWideViewPort()) {
1825            return;
1826        }
1827
1828        if (mBrowserFrame.firstLayoutDone() == false) {
1829            mRestoredScreenWidthScale = scale;
1830        }
1831    }
1832
1833    // called by JNI
1834    private void needTouchEvents(boolean need) {
1835        if (mWebView != null) {
1836            Message.obtain(mWebView.mPrivateHandler,
1837                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
1838                    .sendToTarget();
1839        }
1840    }
1841
1842    // called by JNI
1843    private void updateTextfield(int ptr, boolean changeToPassword,
1844            String text, int textGeneration) {
1845        if (mWebView != null) {
1846            Message msg = Message.obtain(mWebView.mPrivateHandler,
1847                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
1848                    textGeneration, text);
1849            msg.getData().putBoolean("password", changeToPassword);
1850            msg.sendToTarget();
1851        }
1852    }
1853
1854    // called by JNI
1855    private void clearTextEntry() {
1856        if (mWebView == null) return;
1857        Message.obtain(mWebView.mPrivateHandler,
1858                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
1859    }
1860
1861    /**
1862     * Scroll the focused textfield to (x, y) in document space
1863     */
1864    private native void nativeScrollFocusedTextInput(int x, int y);
1865
1866    // these must be in document space (i.e. not scaled/zoomed).
1867    private native void nativeSetScrollOffset(int gen, int dx, int dy);
1868
1869    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
1870
1871    // called by JNI
1872    private void requestListBox(String[] array, boolean[] enabledArray,
1873            int[] selectedArray) {
1874        if (mWebView != null) {
1875            mWebView.requestListBox(array, enabledArray, selectedArray);
1876        }
1877    }
1878
1879    // called by JNI
1880    private void requestListBox(String[] array, boolean[] enabledArray,
1881            int selection) {
1882        if (mWebView != null) {
1883            mWebView.requestListBox(array, enabledArray, selection);
1884        }
1885
1886    }
1887
1888    // called by JNI
1889    private void requestKeyboard(boolean showKeyboard) {
1890        if (mWebView != null) {
1891            Message.obtain(mWebView.mPrivateHandler,
1892                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
1893                    .sendToTarget();
1894        }
1895    }
1896
1897    // This class looks like a SurfaceView to native code. In java, we can
1898    // assume the passed in SurfaceView is this class so we can talk to the
1899    // ViewManager through the ChildView.
1900    private class SurfaceViewProxy extends SurfaceView
1901            implements SurfaceHolder.Callback {
1902        private final ViewManager.ChildView mChildView;
1903        private int mPointer;
1904        SurfaceViewProxy(Context context, ViewManager.ChildView childView,
1905                int pointer) {
1906            super(context);
1907            setWillNotDraw(false); // this prevents the black box artifact
1908            getHolder().addCallback(this);
1909            mChildView = childView;
1910            mChildView.mView = this;
1911            mPointer = pointer;
1912        }
1913        void destroy() {
1914            mPointer = 0;
1915            mChildView.removeView();
1916        }
1917        void attach(int x, int y, int width, int height) {
1918            mChildView.attachView(x, y, width, height);
1919        }
1920
1921        // SurfaceHolder.Callback methods
1922        public void surfaceCreated(SurfaceHolder holder) {
1923            if (mPointer != 0) {
1924                nativeSurfaceChanged(mPointer, 0, 0, 0, 0);
1925            }
1926        }
1927        public void surfaceChanged(SurfaceHolder holder, int format, int width,
1928                int height) {
1929            if (mPointer != 0) {
1930                nativeSurfaceChanged(mPointer, 1, format, width, height);
1931            }
1932        }
1933        public void surfaceDestroyed(SurfaceHolder holder) {
1934            if (mPointer != 0) {
1935                nativeSurfaceChanged(mPointer, 2, 0, 0, 0);
1936            }
1937        }
1938    }
1939
1940    // PluginWidget functions for mainting SurfaceViews for the Surface drawing
1941    // model.
1942    private SurfaceView createSurface(int nativePointer) {
1943        if (mWebView == null) {
1944            return null;
1945        }
1946        return new SurfaceViewProxy(mContext,
1947                mWebView.mViewManager.createView(), nativePointer);
1948    }
1949
1950    private void destroySurface(SurfaceView surface) {
1951        SurfaceViewProxy proxy = (SurfaceViewProxy) surface;
1952        proxy.destroy();
1953    }
1954
1955    private void attachSurface(SurfaceView surface, int x, int y,
1956            int width, int height) {
1957        SurfaceViewProxy proxy = (SurfaceViewProxy) surface;
1958        proxy.attach(x, y, width, height);
1959    }
1960
1961    // Callback for the SurfaceHolder.Callback. Called for all the surface
1962    // callbacks. The state parameter is one of Created(0), Changed(1),
1963    // Destroyed(2).
1964    private native void nativeSurfaceChanged(int pointer, int state, int format,
1965            int width, int height);
1966
1967    private native void nativePause();
1968    private native void nativeResume();
1969    private native void nativeFreeMemory();
1970}
1971