WebViewCore.java revision c39a6e0c51e182338deb8b63d07933b585134929
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.Config;
33import android.util.Log;
34import android.util.SparseBooleanArray;
35import android.view.KeyEvent;
36
37import java.util.ArrayList;
38import java.util.HashMap;
39
40import junit.framework.Assert;
41
42final class WebViewCore {
43
44    private static final String LOGTAG = "webcore";
45    static final boolean DEBUG = false;
46    static final boolean LOGV_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
47
48    static {
49        // Load libwebcore during static initialization. This happens in the
50        // zygote process so it will be shared read-only across all app
51        // processes.
52        System.loadLibrary("webcore");
53    }
54
55    /*
56     * WebViewCore always executes in the same thread as the native webkit.
57     */
58
59    // The WebView that corresponds to this WebViewCore.
60    private WebView mWebView;
61    // Proxy for handling callbacks from native code
62    private final CallbackProxy mCallbackProxy;
63    // Settings object for maintaining all settings
64    private final WebSettings mSettings;
65    // Context for initializing the BrowserFrame with the proper assets.
66    private final Context mContext;
67    // The pointer to a native view object.
68    private int mNativeClass;
69    // The BrowserFrame is an interface to the native Frame component.
70    private BrowserFrame mBrowserFrame;
71
72    /*
73     * range is from 200 to 10,000. 0 is a special value means device-width. -1
74     * means undefined.
75     */
76    private int mViewportWidth = -1;
77
78    /*
79     * range is from 200 to 10,000. 0 is a special value means device-height. -1
80     * means undefined.
81     */
82    private int mViewportHeight = -1;
83
84    /*
85     * scale in percent, range is from 1 to 1000. 0 means undefined.
86     */
87    private int mViewportInitialScale = 0;
88
89    /*
90     * scale in percent, range is from 1 to 1000. 0 means undefined.
91     */
92    private int mViewportMinimumScale = 0;
93
94    /*
95     * scale in percent, range is from 1 to 1000. 0 means undefined.
96     */
97    private int mViewportMaximumScale = 0;
98
99    private boolean mViewportUserScalable = true;
100
101    private int mRestoredScale = 100;
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);
144        // The WebIconDatabase needs to be initialized within the UI thread so
145        // just request the instance here.
146        WebIconDatabase.getInstance();
147        // Send a message to initialize the WebViewCore.
148        Message init = sWebCoreHandler.obtainMessage(
149                WebCoreThread.INITIALIZE, this);
150        sWebCoreHandler.sendMessage(init);
151    }
152
153    /* Initialize private data within the WebCore thread.
154     */
155    private void initialize() {
156        /* Initialize our private BrowserFrame class to handle all
157         * frame-related functions. We need to create a new view which
158         * in turn creates a C level FrameView and attaches it to the frame.
159         */
160        mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy,
161                mSettings);
162        // Sync the native settings and also create the WebCore thread handler.
163        mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
164        // Create the handler and transfer messages for the IconDatabase
165        WebIconDatabase.getInstance().createHandler();
166        // The transferMessages call will transfer all pending messages to the
167        // WebCore thread handler.
168        mEventHub.transferMessages();
169
170        // Send a message back to WebView to tell it that we have set up the
171        // WebCore thread.
172        if (mWebView != null) {
173            Message.obtain(mWebView.mPrivateHandler,
174                    WebView.WEBCORE_INITIALIZED_MSG_ID,
175                    mNativeClass, 0).sendToTarget();
176        }
177
178    }
179
180    /* Handle the initialization of WebViewCore during subwindow creation. This
181     * method is called from the WebCore thread but it is called before the
182     * INITIALIZE message can be handled.
183     */
184    /* package */ void initializeSubwindow() {
185        // Go ahead and initialize the core components.
186        initialize();
187        // Remove the INITIALIZE method so we don't try to initialize twice.
188        sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this);
189    }
190
191    /* Get the BrowserFrame component. This is used for subwindow creation and
192     * is called only from BrowserFrame in the WebCore thread. */
193    /* package */ BrowserFrame getBrowserFrame() {
194        return mBrowserFrame;
195    }
196
197    //-------------------------------------------------------------------------
198    // Common methods
199    //-------------------------------------------------------------------------
200
201    /**
202     * Causes all timers to pause. This applies to all WebViews in the current
203     * app process.
204     */
205    public static void pauseTimers() {
206        if (BrowserFrame.sJavaBridge == null) {
207            throw new IllegalStateException(
208                    "No WebView has been created in this process!");
209        }
210        BrowserFrame.sJavaBridge.pause();
211    }
212
213    /**
214     * Resume all timers. This applies to all WebViews in the current process.
215     */
216    public static void resumeTimers() {
217        if (BrowserFrame.sJavaBridge == null) {
218            throw new IllegalStateException(
219                    "No WebView has been created in this process!");
220        }
221        BrowserFrame.sJavaBridge.resume();
222    }
223
224    public WebSettings getSettings() {
225        return mSettings;
226    }
227
228    /**
229     * Invoke a javascript alert.
230     * @param message The message displayed in the alert.
231     */
232    protected void jsAlert(String url, String message) {
233        mCallbackProxy.onJsAlert(url, message);
234    }
235
236    /**
237     * Invoke a javascript confirm dialog.
238     * @param message The message displayed in the dialog.
239     * @return True if the user confirmed or false if the user cancelled.
240     */
241    protected boolean jsConfirm(String url, String message) {
242        return mCallbackProxy.onJsConfirm(url, message);
243    }
244
245    /**
246     * Invoke a javascript prompt dialog.
247     * @param message The message to be displayed in the dialog.
248     * @param defaultValue The default value in the prompt input.
249     * @return The input from the user or null to indicate the user cancelled
250     *         the dialog.
251     */
252    protected String jsPrompt(String url, String message, String defaultValue) {
253        return mCallbackProxy.onJsPrompt(url, message, defaultValue);
254    }
255
256    /**
257     * Invoke a javascript before unload dialog.
258     * @param url The url that is requesting the dialog.
259     * @param message The message displayed in the dialog.
260     * @return True if the user confirmed or false if the user cancelled. False
261     *         will cancel the navigation.
262     */
263    protected boolean jsUnload(String url, String message) {
264        return mCallbackProxy.onJsBeforeUnload(url, message);
265    }
266
267    //-------------------------------------------------------------------------
268    // JNI methods
269    //-------------------------------------------------------------------------
270
271    static native String nativeFindAddress(String addr);
272
273    /**
274     * Empty the picture set.
275     */
276    private native void nativeClearContent();
277
278    /**
279     * Create a flat picture from the set of pictures.
280     */
281    private native void nativeCopyContentToPicture(Picture picture);
282
283    /**
284     * Draw the picture set with a background color. Returns true
285     * if some individual picture took too long to draw and can be
286     * split into parts. Called from the UI thread.
287     */
288    private native boolean nativeDrawContent(Canvas canvas, int color);
289
290    /**
291     * Redraw a portion of the picture set. The Point wh returns the
292     * width and height of the overall picture.
293     */
294    private native boolean nativeRecordContent(Region invalRegion, Point wh);
295
296    /**
297     * Splits slow parts of the picture set. Called from the webkit
298     * thread after nativeDrawContent returns true.
299     */
300    private native void nativeSplitContent();
301
302    private native boolean nativeKey(int keyCode, int unichar,
303            int repeatCount, boolean isShift, boolean isAlt, boolean isDown);
304
305    private native boolean nativeClick();
306
307    private native void nativeSendListBoxChoices(boolean[] choices, int size);
308
309    private native void nativeSendListBoxChoice(int choice);
310
311    /*  Tell webkit what its width and height are, for the purposes
312        of layout/line-breaking. These coordinates are in document space,
313        which is the same as View coords unless we have zoomed the document
314        (see nativeSetZoom).
315        screenWidth is used by layout to wrap column around. If viewport uses
316        fixed size, screenWidth can be different from width with zooming.
317        should this be called nativeSetViewPortSize?
318    */
319    private native void nativeSetSize(int width, int height, int screenWidth,
320            float scale, int realScreenWidth, int screenHeight);
321
322    private native int nativeGetContentMinPrefWidth();
323
324    // Start: functions that deal with text editing
325    private native void nativeReplaceTextfieldText(int frame, int node, int x,
326            int y, int oldStart, int oldEnd, String replace, int newStart,
327            int newEnd);
328
329    private native void passToJs(int frame, int node, int x, int y, int gen,
330            String currentText, int keyCode, int keyValue, boolean down,
331            boolean cap, boolean fn, boolean sym);
332
333    private native void nativeSaveDocumentState(int frame);
334
335    private native void nativeSetFinalFocus(int framePtr, int nodePtr, int x,
336            int y, boolean block);
337
338    private native void nativeSetKitFocus(int moveGeneration,
339            int buildGeneration, int framePtr, int nodePtr, int x, int y,
340            boolean ignoreNullFocus);
341
342    private native String nativeRetrieveHref(int framePtr, int nodePtr);
343
344    private native void nativeTouchUp(int touchGeneration,
345            int buildGeneration, int framePtr, int nodePtr, int x, int y,
346            int size, boolean isClick, boolean retry);
347
348    private native boolean nativeHandleTouchEvent(int action, int x, int y);
349
350    private native void nativeUnblockFocus();
351
352    private native void nativeUpdateFrameCache();
353
354    private native void nativeSetSnapAnchor(int x, int y);
355
356    private native void nativeSnapToAnchor();
357
358    private native void nativeSetBackgroundColor(int color);
359
360    private native void nativeDumpDomTree(boolean useFile);
361
362    private native void nativeDumpRenderTree(boolean useFile);
363
364    private native void nativeDumpNavTree();
365
366    private native void nativeRefreshPlugins(boolean reloadOpenPages);
367
368    /**
369     *  Delete text from start to end in the focused textfield. If there is no
370     *  focus, or if start == end, silently fail.  If start and end are out of
371     *  order, swap them.
372     *  @param  start   Beginning of selection to delete.
373     *  @param  end     End of selection to delete.
374     */
375    private native void nativeDeleteSelection(int frame, int node, int x, int y,
376        int start, int end);
377
378    /**
379     *  Set the selection to (start, end) in the focused textfield. If start and
380     *  end are out of order, swap them.
381     *  @param  start   Beginning of selection.
382     *  @param  end     End of selection.
383     */
384    private native void nativeSetSelection(int frame, int node, int x, int y,
385        int start, int end);
386
387    private native String nativeGetSelection(Region sel);
388
389    // Register a scheme to be treated as local scheme so that it can access
390    // local asset files for resources
391    private native void nativeRegisterURLSchemeAsLocal(String scheme);
392
393    // EventHub for processing messages
394    private final EventHub mEventHub;
395    // WebCore thread handler
396    private static Handler sWebCoreHandler;
397    // Class for providing Handler creation inside the WebCore thread.
398    private static class WebCoreThread implements Runnable {
399        // Message id for initializing a new WebViewCore.
400        private static final int INITIALIZE = 0;
401        private static final int REDUCE_PRIORITY = 1;
402        private static final int RESUME_PRIORITY = 2;
403        private static final int CACHE_TICKER = 3;
404        private static final int BLOCK_CACHE_TICKER = 4;
405        private static final int RESUME_CACHE_TICKER = 5;
406
407        private static final int CACHE_TICKER_INTERVAL = 60 * 1000; // 1 minute
408
409        private static boolean mCacheTickersBlocked = true;
410
411        public void run() {
412            Looper.prepare();
413            Assert.assertNull(sWebCoreHandler);
414            synchronized (WebViewCore.class) {
415                sWebCoreHandler = new Handler() {
416                    @Override
417                    public void handleMessage(Message msg) {
418                        switch (msg.what) {
419                            case INITIALIZE:
420                                WebViewCore core = (WebViewCore) msg.obj;
421                                core.initialize();
422                                break;
423
424                            case REDUCE_PRIORITY:
425                                // 3 is an adjustable number.
426                                Process.setThreadPriority(
427                                        Process.THREAD_PRIORITY_DEFAULT + 3 *
428                                        Process.THREAD_PRIORITY_LESS_FAVORABLE);
429                                break;
430
431                            case RESUME_PRIORITY:
432                                Process.setThreadPriority(
433                                        Process.THREAD_PRIORITY_DEFAULT);
434                                break;
435
436                            case CACHE_TICKER:
437                                if (!mCacheTickersBlocked) {
438                                    CacheManager.endCacheTransaction();
439                                    CacheManager.startCacheTransaction();
440                                    sendMessageDelayed(
441                                            obtainMessage(CACHE_TICKER),
442                                            CACHE_TICKER_INTERVAL);
443                                }
444                                break;
445
446                            case BLOCK_CACHE_TICKER:
447                                if (CacheManager.endCacheTransaction()) {
448                                    mCacheTickersBlocked = true;
449                                }
450                                break;
451
452                            case RESUME_CACHE_TICKER:
453                                if (CacheManager.startCacheTransaction()) {
454                                    mCacheTickersBlocked = false;
455                                }
456                                break;
457                        }
458                    }
459                };
460                WebViewCore.class.notify();
461            }
462            Looper.loop();
463        }
464    }
465
466    static class FocusData {
467        FocusData() {}
468        FocusData(FocusData d) {
469            mMoveGeneration = d.mMoveGeneration;
470            mBuildGeneration = d.mBuildGeneration;
471            mFrame = d.mFrame;
472            mNode = d.mNode;
473            mX = d.mX;
474            mY = d.mY;
475            mIgnoreNullFocus = d.mIgnoreNullFocus;
476        }
477        int mMoveGeneration;
478        int mBuildGeneration;
479        int mFrame;
480        int mNode;
481        int mX;
482        int mY;
483        boolean mIgnoreNullFocus;
484    }
485
486    static class TouchUpData {
487        int mMoveGeneration;
488        int mBuildGeneration;
489        int mFrame;
490        int mNode;
491        int mX;
492        int mY;
493        int mSize;
494        boolean mIsClick;
495        boolean mRetry;
496    }
497
498    static class TouchEventData {
499        int mAction;    // MotionEvent.getAction()
500        int mX;
501        int mY;
502    }
503
504        static final String[] HandlerDebugString = {
505            "LOAD_URL", // = 100;
506            "STOP_LOADING", // = 101;
507            "RELOAD", // = 102;
508            "KEY_DOWN", // = 103;
509            "KEY_UP", // = 104;
510            "VIEW_SIZE_CHANGED", // = 105;
511            "GO_BACK_FORWARD", // = 106;
512            "SET_SCROLL_OFFSET", // = 107;
513            "RESTORE_STATE", // = 108;
514            "PAUSE_TIMERS", // = 109;
515            "RESUME_TIMERS", // = 110;
516            "CLEAR_CACHE", // = 111;
517            "CLEAR_HISTORY", // = 112;
518            "SET_SELECTION", // = 113;
519            "REPLACE_TEXT", // = 114;
520            "PASS_TO_JS", // = 115;
521            "SET_GLOBAL_BOUNDS", // = 116;
522            "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117;
523            "CLICK", // = 118;
524            "119",
525            "DOC_HAS_IMAGES", // = 120;
526            "SET_SNAP_ANCHOR", // = 121;
527            "DELETE_SELECTION", // = 122;
528            "LISTBOX_CHOICES", // = 123;
529            "SINGLE_LISTBOX_CHOICE", // = 124;
530            "125",
531            "SET_BACKGROUND_COLOR", // = 126;
532            "UNBLOCK_FOCUS", // = 127;
533            "SAVE_DOCUMENT_STATE", // = 128;
534            "GET_SELECTION", // = 129;
535            "WEBKIT_DRAW", // = 130;
536            "SYNC_SCROLL", // = 131;
537            "REFRESH_PLUGINS", // = 132;
538            "SPLIT_PICTURE_SET", // = 133;
539            "CLEAR_CONTENT", // = 134;
540            "SET_FINAL_FOCUS", // = 135;
541            "SET_KIT_FOCUS", // = 136;
542            "REQUEST_FOCUS_HREF", // = 137;
543            "ADD_JS_INTERFACE", // = 138;
544            "LOAD_DATA", // = 139;
545            "TOUCH_UP", // = 140;
546            "TOUCH_EVENT", // = 141;
547        };
548
549    class EventHub {
550        // Message Ids
551        static final int LOAD_URL = 100;
552        static final int STOP_LOADING = 101;
553        static final int RELOAD = 102;
554        static final int KEY_DOWN = 103;
555        static final int KEY_UP = 104;
556        static final int VIEW_SIZE_CHANGED = 105;
557        static final int GO_BACK_FORWARD = 106;
558        static final int SET_SCROLL_OFFSET = 107;
559        static final int RESTORE_STATE = 108;
560        static final int PAUSE_TIMERS = 109;
561        static final int RESUME_TIMERS = 110;
562        static final int CLEAR_CACHE = 111;
563        static final int CLEAR_HISTORY = 112;
564        static final int SET_SELECTION = 113;
565        static final int REPLACE_TEXT = 114;
566        static final int PASS_TO_JS = 115;
567        static final int SET_GLOBAL_BOUNDS = 116;
568        static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117;
569        static final int CLICK = 118;
570        static final int DOC_HAS_IMAGES = 120;
571        static final int SET_SNAP_ANCHOR = 121;
572        static final int DELETE_SELECTION = 122;
573        static final int LISTBOX_CHOICES = 123;
574        static final int SINGLE_LISTBOX_CHOICE = 124;
575        static final int SET_BACKGROUND_COLOR = 126;
576        static final int UNBLOCK_FOCUS = 127;
577        static final int SAVE_DOCUMENT_STATE = 128;
578        static final int GET_SELECTION = 129;
579        static final int WEBKIT_DRAW = 130;
580        static final int SYNC_SCROLL = 131;
581        static final int REFRESH_PLUGINS = 132;
582        static final int SPLIT_PICTURE_SET = 133;
583        static final int CLEAR_CONTENT = 134;
584
585        // UI nav messages
586        static final int SET_FINAL_FOCUS = 135;
587        static final int SET_KIT_FOCUS = 136;
588        static final int REQUEST_FOCUS_HREF = 137;
589        static final int ADD_JS_INTERFACE = 138;
590        static final int LOAD_DATA = 139;
591
592        // motion
593        static final int TOUCH_UP = 140;
594        // message used to pass UI touch events to WebCore
595        static final int TOUCH_EVENT = 141;
596
597        // Network-based messaging
598        static final int CLEAR_SSL_PREF_TABLE = 150;
599
600        // Test harness messages
601        static final int REQUEST_EXT_REPRESENTATION = 160;
602        static final int REQUEST_DOC_AS_TEXT = 161;
603
604        // debugging
605        static final int DUMP_DOMTREE = 170;
606        static final int DUMP_RENDERTREE = 171;
607        static final int DUMP_NAVTREE = 172;
608
609        // private message ids
610        private static final int DESTROY =     200;
611
612        // flag values passed to message SET_FINAL_FOCUS
613        static final int NO_FOCUS_CHANGE_BLOCK = 0;
614        static final int BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP = 1;
615
616        // Private handler for WebCore messages.
617        private Handler mHandler;
618        // Message queue for containing messages before the WebCore thread is
619        // ready.
620        private ArrayList<Message> mMessages = new ArrayList<Message>();
621        // Flag for blocking messages. This is used during DESTROY to avoid
622        // posting more messages to the EventHub or to WebView's event handler.
623        private boolean mBlockMessages;
624
625        private int mTid;
626        private int mSavedPriority;
627
628        /**
629         * Prevent other classes from creating an EventHub.
630         */
631        private EventHub() {}
632
633        /**
634         * Transfer all messages to the newly created webcore thread handler.
635         */
636        private void transferMessages() {
637            mTid = Process.myTid();
638            mSavedPriority = Process.getThreadPriority(mTid);
639
640            mHandler = new Handler() {
641                @Override
642                public void handleMessage(Message msg) {
643                    if (LOGV_ENABLED) {
644                        Log.v(LOGTAG, msg.what < LOAD_URL || msg.what
645                                > TOUCH_EVENT ? Integer.toString(msg.what)
646                                : HandlerDebugString[msg.what - LOAD_URL]);
647                    }
648                    switch (msg.what) {
649                        case WEBKIT_DRAW:
650                            webkitDraw();
651                            break;
652
653                        case DESTROY:
654                            // Time to take down the world. Cancel all pending
655                            // loads and destroy the native view and frame.
656                            mBrowserFrame.destroy();
657                            mBrowserFrame = null;
658                            mNativeClass = 0;
659                            break;
660
661                        case LOAD_URL:
662                            loadUrl((String) msg.obj);
663                            break;
664
665                        case LOAD_DATA:
666                            HashMap loadParams = (HashMap) msg.obj;
667                            String baseUrl = (String) loadParams.get("baseUrl");
668                            if (baseUrl != null) {
669                                int i = baseUrl.indexOf(':');
670                                if (i > 0) {
671                                    /*
672                                     * In 1.0, {@link
673                                     * WebView#loadDataWithBaseURL} can access
674                                     * local asset files as long as the data is
675                                     * valid. In the new WebKit, the restriction
676                                     * is tightened. To be compatible with 1.0,
677                                     * we automatically add the scheme of the
678                                     * baseUrl for local access as long as it is
679                                     * not http(s)/ftp(s)/about/javascript
680                                     */
681                                    String scheme = baseUrl.substring(0, i);
682                                    if (!scheme.startsWith("http") &&
683                                            !scheme.startsWith("ftp") &&
684                                            !scheme.startsWith("about") &&
685                                            !scheme.startsWith("javascript")) {
686                                        nativeRegisterURLSchemeAsLocal(scheme);
687                                    }
688                                }
689                            }
690                            mBrowserFrame.loadData(baseUrl,
691                                    (String) loadParams.get("data"),
692                                    (String) loadParams.get("mimeType"),
693                                    (String) loadParams.get("encoding"),
694                                    (String) loadParams.get("failUrl"));
695                            break;
696
697                        case STOP_LOADING:
698                            // If the WebCore has committed the load, but not
699                            // finished the first layout yet, we need to set
700                            // first layout done to trigger the interpreted side sync
701                            // up with native side
702                            if (mBrowserFrame.committed()
703                                    && !mBrowserFrame.firstLayoutDone()) {
704                                mBrowserFrame.didFirstLayout();
705                            }
706                            // Do this after syncing up the layout state.
707                            stopLoading();
708                            break;
709
710                        case RELOAD:
711                            mBrowserFrame.reload(false);
712                            break;
713
714                        case KEY_DOWN:
715                            key((KeyEvent) msg.obj, true);
716                            break;
717
718                        case KEY_UP:
719                            key((KeyEvent) msg.obj, false);
720                            break;
721
722                        case CLICK:
723                            nativeClick();
724                            break;
725
726                        case VIEW_SIZE_CHANGED:
727                            viewSizeChanged(msg.arg1, msg.arg2,
728                                    ((Integer) msg.obj).intValue());
729                            break;
730
731                        case SET_SCROLL_OFFSET:
732                            // note: these are in document coordinates
733                            // (inv-zoom)
734                            nativeSetScrollOffset(msg.arg1, msg.arg2);
735                            break;
736
737                        case SET_GLOBAL_BOUNDS:
738                            Rect r = (Rect) msg.obj;
739                            nativeSetGlobalBounds(r.left, r.top, r.width(),
740                                r.height());
741                            break;
742
743                        case GO_BACK_FORWARD:
744                            // If it is a standard load and the load is not
745                            // committed yet, we interpret BACK as RELOAD
746                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
747                                    (mBrowserFrame.loadType() ==
748                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
749                                mBrowserFrame.reload(true);
750                            } else {
751                                mBrowserFrame.goBackOrForward(msg.arg1);
752                            }
753                            break;
754
755                        case RESTORE_STATE:
756                            stopLoading();
757                            restoreState(msg.arg1);
758                            break;
759
760                        case PAUSE_TIMERS:
761                            mSavedPriority = Process.getThreadPriority(mTid);
762                            Process.setThreadPriority(mTid,
763                                    Process.THREAD_PRIORITY_BACKGROUND);
764                            pauseTimers();
765                            if (CacheManager.disableTransaction()) {
766                                WebCoreThread.mCacheTickersBlocked = true;
767                                sWebCoreHandler.removeMessages(
768                                        WebCoreThread.CACHE_TICKER);
769                            }
770                            break;
771
772                        case RESUME_TIMERS:
773                            Process.setThreadPriority(mTid, mSavedPriority);
774                            resumeTimers();
775                            if (CacheManager.enableTransaction()) {
776                                WebCoreThread.mCacheTickersBlocked = false;
777                                sWebCoreHandler.sendMessageDelayed(
778                                        sWebCoreHandler.obtainMessage(
779                                        WebCoreThread.CACHE_TICKER),
780                                        WebCoreThread.CACHE_TICKER_INTERVAL);
781                            }
782                            break;
783
784                        case CLEAR_CACHE:
785                            mBrowserFrame.clearCache();
786                            if (msg.arg1 == 1) {
787                                CacheManager.removeAllCacheFiles();
788                            }
789                            break;
790
791                        case CLEAR_HISTORY:
792                            mCallbackProxy.getBackForwardList().
793                                    close(mBrowserFrame.mNativeFrame);
794                            break;
795
796                        case REPLACE_TEXT:
797                            HashMap jMap = (HashMap) msg.obj;
798                            FocusData fData = (FocusData) jMap.get("focusData");
799                            String replace = (String) jMap.get("replace");
800                            int newStart =
801                                    ((Integer) jMap.get("start")).intValue();
802                            int newEnd =
803                                    ((Integer) jMap.get("end")).intValue();
804                            nativeReplaceTextfieldText(fData.mFrame,
805                                    fData.mNode, fData.mX, fData.mY, msg.arg1,
806                                    msg.arg2, replace, newStart, newEnd);
807                            break;
808
809                        case PASS_TO_JS: {
810                            HashMap jsMap = (HashMap) msg.obj;
811                            FocusData fDat = (FocusData) jsMap.get("focusData");
812                            KeyEvent evt = (KeyEvent) jsMap.get("event");
813                            int keyCode = evt.getKeyCode();
814                            int keyValue = evt.getUnicodeChar();
815                            int generation = msg.arg1;
816                            passToJs(fDat.mFrame, fDat.mNode, fDat.mX, fDat.mY,
817                                    generation,
818                                    (String) jsMap.get("currentText"),
819                                    keyCode,
820                                    keyValue,
821                                    evt.isDown(),
822                                    evt.isShiftPressed(), evt.isAltPressed(),
823                                    evt.isSymPressed());
824                            break;
825                        }
826
827                        case SAVE_DOCUMENT_STATE: {
828                            FocusData fDat = (FocusData) msg.obj;
829                            nativeSaveDocumentState(fDat.mFrame);
830                            break;
831                        }
832
833                        case CLEAR_SSL_PREF_TABLE:
834                            Network.getInstance(mContext)
835                                    .clearUserSslPrefTable();
836                            break;
837
838                        case TOUCH_UP:
839                            TouchUpData touchUpData = (TouchUpData) msg.obj;
840                            nativeTouchUp(touchUpData.mMoveGeneration,
841                                    touchUpData.mBuildGeneration,
842                                    touchUpData.mFrame, touchUpData.mNode,
843                                    touchUpData.mX, touchUpData.mY,
844                                    touchUpData.mSize, touchUpData.mIsClick,
845                                    touchUpData.mRetry);
846                            break;
847
848                        case TOUCH_EVENT: {
849                            TouchEventData ted = (TouchEventData) msg.obj;
850                            Message.obtain(
851                                    mWebView.mPrivateHandler,
852                                    WebView.PREVENT_TOUCH_ID, ted.mAction,
853                                    nativeHandleTouchEvent(ted.mAction, ted.mX,
854                                            ted.mY) ? 1 : 0).sendToTarget();
855                            break;
856                        }
857
858                        case ADD_JS_INTERFACE:
859                            HashMap map = (HashMap) msg.obj;
860                            Object obj = map.get("object");
861                            String interfaceName = (String)
862                                    map.get("interfaceName");
863                            mBrowserFrame.addJavascriptInterface(obj,
864                                    interfaceName);
865                            break;
866
867                        case REQUEST_EXT_REPRESENTATION:
868                            mBrowserFrame.externalRepresentation(
869                                    (Message) msg.obj);
870                            break;
871
872                        case REQUEST_DOC_AS_TEXT:
873                            mBrowserFrame.documentAsText((Message) msg.obj);
874                            break;
875
876                        case SET_FINAL_FOCUS:
877                            FocusData finalData = (FocusData) msg.obj;
878                            nativeSetFinalFocus(finalData.mFrame,
879                                     finalData.mNode, finalData.mX,
880                                     finalData.mY, msg.arg1
881                                     != EventHub.NO_FOCUS_CHANGE_BLOCK);
882                            break;
883
884                        case UNBLOCK_FOCUS:
885                            nativeUnblockFocus();
886                            break;
887
888                        case SET_KIT_FOCUS:
889                            FocusData focusData = (FocusData) msg.obj;
890                            nativeSetKitFocus(focusData.mMoveGeneration,
891                                    focusData.mBuildGeneration,
892                                    focusData.mFrame, focusData.mNode,
893                                    focusData.mX, focusData.mY,
894                                    focusData.mIgnoreNullFocus);
895                            break;
896
897                        case REQUEST_FOCUS_HREF: {
898                            Message hrefMsg = (Message) msg.obj;
899                            String res = nativeRetrieveHref(msg.arg1, msg.arg2);
900                            hrefMsg.getData().putString("url", res);
901                            hrefMsg.sendToTarget();
902                            break;
903                        }
904
905                        case UPDATE_CACHE_AND_TEXT_ENTRY:
906                            nativeUpdateFrameCache();
907                            // FIXME: this should provide a minimal rectangle
908                            if (mWebView != null) {
909                                mWebView.postInvalidate();
910                            }
911                            sendUpdateTextEntry();
912                            break;
913
914                        case DOC_HAS_IMAGES:
915                            Message imageResult = (Message) msg.obj;
916                            imageResult.arg1 =
917                                    mBrowserFrame.documentHasImages() ? 1 : 0;
918                            imageResult.sendToTarget();
919                            break;
920
921                        case SET_SNAP_ANCHOR:
922                            nativeSetSnapAnchor(msg.arg1, msg.arg2);
923                            break;
924
925                        case DELETE_SELECTION:
926                            FocusData delData = (FocusData) msg.obj;
927                            nativeDeleteSelection(delData.mFrame,
928                                     delData.mNode, delData.mX,
929                                     delData.mY, msg.arg1, msg.arg2);
930                            break;
931
932                        case SET_SELECTION:
933                            FocusData selData = (FocusData) msg.obj;
934                            nativeSetSelection(selData.mFrame,
935                                     selData.mNode, selData.mX,
936                                     selData.mY, msg.arg1, msg.arg2);
937                            break;
938
939                        case LISTBOX_CHOICES:
940                            SparseBooleanArray choices = (SparseBooleanArray)
941                                    msg.obj;
942                            int choicesSize = msg.arg1;
943                            boolean[] choicesArray = new boolean[choicesSize];
944                            for (int c = 0; c < choicesSize; c++) {
945                                choicesArray[c] = choices.get(c);
946                            }
947                            nativeSendListBoxChoices(choicesArray,
948                                    choicesSize);
949                            break;
950
951                        case SINGLE_LISTBOX_CHOICE:
952                            nativeSendListBoxChoice(msg.arg1);
953                            break;
954
955                        case SET_BACKGROUND_COLOR:
956                            nativeSetBackgroundColor(msg.arg1);
957                            break;
958
959                        case GET_SELECTION:
960                            String str = nativeGetSelection((Region) msg.obj);
961                            Message.obtain(mWebView.mPrivateHandler
962                                    , WebView.UPDATE_CLIPBOARD, str)
963                                    .sendToTarget();
964                            break;
965
966                        case DUMP_DOMTREE:
967                            nativeDumpDomTree(msg.arg1 == 1);
968                            break;
969
970                        case DUMP_RENDERTREE:
971                            nativeDumpRenderTree(msg.arg1 == 1);
972                            break;
973
974                        case DUMP_NAVTREE:
975                            nativeDumpNavTree();
976                            break;
977
978                        case SYNC_SCROLL:
979                            mWebkitScrollX = msg.arg1;
980                            mWebkitScrollY = msg.arg2;
981                            break;
982
983                        case REFRESH_PLUGINS:
984                            nativeRefreshPlugins(msg.arg1 != 0);
985                            break;
986
987                        case SPLIT_PICTURE_SET:
988                            nativeSplitContent();
989                            mSplitPictureIsScheduled = false;
990                            break;
991
992                        case CLEAR_CONTENT:
993                            // Clear the view so that onDraw() will draw nothing
994                            // but white background
995                            // (See public method WebView.clearView)
996                            nativeClearContent();
997                            break;
998                    }
999                }
1000            };
1001            // Take all queued messages and resend them to the new handler.
1002            synchronized (this) {
1003                int size = mMessages.size();
1004                for (int i = 0; i < size; i++) {
1005                    mHandler.sendMessage(mMessages.get(i));
1006                }
1007                mMessages = null;
1008            }
1009        }
1010
1011        /**
1012         * Send a message internally to the queue or to the handler
1013         */
1014        private synchronized void sendMessage(Message msg) {
1015            if (mBlockMessages) {
1016                return;
1017            }
1018            if (mMessages != null) {
1019                mMessages.add(msg);
1020            } else {
1021                mHandler.sendMessage(msg);
1022            }
1023        }
1024
1025        private synchronized void removeMessages(int what) {
1026            if (mBlockMessages) {
1027                return;
1028            }
1029            if (what == EventHub.WEBKIT_DRAW) {
1030                mDrawIsScheduled = false;
1031            }
1032            if (mMessages != null) {
1033                Log.w(LOGTAG, "Not supported in this case.");
1034            } else {
1035                mHandler.removeMessages(what);
1036            }
1037        }
1038
1039        private synchronized void sendMessageDelayed(Message msg, long delay) {
1040            if (mBlockMessages) {
1041                return;
1042            }
1043            mHandler.sendMessageDelayed(msg, delay);
1044        }
1045
1046        /**
1047         * Send a message internally to the front of the queue.
1048         */
1049        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1050            if (mBlockMessages) {
1051                return;
1052            }
1053            if (mMessages != null) {
1054                mMessages.add(0, msg);
1055            } else {
1056                mHandler.sendMessageAtFrontOfQueue(msg);
1057            }
1058        }
1059
1060        /**
1061         * Remove all the messages.
1062         */
1063        private synchronized void removeMessages() {
1064            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1065            mDrawIsScheduled = false;
1066            mSplitPictureIsScheduled = false;
1067            if (mMessages != null) {
1068                mMessages.clear();
1069            } else {
1070                mHandler.removeCallbacksAndMessages(null);
1071            }
1072        }
1073
1074        /**
1075         * Block sending messages to the EventHub.
1076         */
1077        private synchronized void blockMessages() {
1078            mBlockMessages = true;
1079        }
1080    }
1081
1082    //-------------------------------------------------------------------------
1083    // Methods called by host activity (in the same thread)
1084    //-------------------------------------------------------------------------
1085
1086    void stopLoading() {
1087        if (LOGV_ENABLED) Log.v(LOGTAG, "CORE stopLoading");
1088        if (mBrowserFrame != null) {
1089            mBrowserFrame.stopLoading();
1090        }
1091    }
1092
1093    //-------------------------------------------------------------------------
1094    // Methods called by WebView
1095    // If it refers to local variable, it needs synchronized().
1096    // If it needs WebCore, it has to send message.
1097    //-------------------------------------------------------------------------
1098
1099    void sendMessage(Message msg) {
1100        mEventHub.sendMessage(msg);
1101    }
1102
1103    void sendMessage(int what) {
1104        mEventHub.sendMessage(Message.obtain(null, what));
1105    }
1106
1107    void sendMessage(int what, Object obj) {
1108        mEventHub.sendMessage(Message.obtain(null, what, obj));
1109    }
1110
1111    void sendMessage(int what, int arg1) {
1112        // just ignore the second argument (make it 0)
1113        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1114    }
1115
1116    void sendMessage(int what, int arg1, int arg2) {
1117        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1118    }
1119
1120    void sendMessage(int what, int arg1, Object obj) {
1121        // just ignore the second argument (make it 0)
1122        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1123    }
1124
1125    void sendMessage(int what, int arg1, int arg2, Object obj) {
1126        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1127    }
1128
1129    void sendMessageDelayed(int what, Object obj, long delay) {
1130        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1131    }
1132
1133    void removeMessages(int what) {
1134        mEventHub.removeMessages(what);
1135    }
1136
1137    void removeMessages() {
1138        mEventHub.removeMessages();
1139    }
1140
1141    /**
1142     * Removes pending messages and trigger a DESTROY message to send to
1143     * WebCore.
1144     * Called from UI thread.
1145     */
1146    void destroy() {
1147        // We don't want anyone to post a message between removing pending
1148        // messages and sending the destroy message.
1149        synchronized (mEventHub) {
1150            mEventHub.removeMessages();
1151            mEventHub.sendMessageAtFrontOfQueue(
1152                    Message.obtain(null, EventHub.DESTROY));
1153            mEventHub.blockMessages();
1154            mWebView = null;
1155        }
1156    }
1157
1158    //-------------------------------------------------------------------------
1159    // WebViewCore private methods
1160    //-------------------------------------------------------------------------
1161
1162    private void loadUrl(String url) {
1163        if (LOGV_ENABLED) Log.v(LOGTAG, " CORE loadUrl " + url);
1164        mBrowserFrame.loadUrl(url);
1165    }
1166
1167    private void key(KeyEvent evt, boolean isDown) {
1168        if (LOGV_ENABLED) {
1169            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1170                    + evt);
1171        }
1172        if (!nativeKey(evt.getKeyCode(), evt.getUnicodeChar(),
1173                evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
1174                isDown)) {
1175            // bubble up the event handling
1176            mCallbackProxy.onUnhandledKeyEvent(evt);
1177        }
1178    }
1179
1180    // These values are used to avoid requesting a layout based on old values
1181    private int mCurrentViewWidth = 0;
1182    private int mCurrentViewHeight = 0;
1183
1184    // Define a minimum screen width so that we won't wrap the paragraph to one
1185    // word per line during zoom-in.
1186    private static final int MIN_SCREEN_WIDTH = 160;
1187
1188    // notify webkit that our virtual view size changed size (after inv-zoom)
1189    private void viewSizeChanged(int w, int h, int viewWidth) {
1190        if (LOGV_ENABLED) Log.v(LOGTAG, "CORE onSizeChanged");
1191        if (w == 0) {
1192            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1193            return;
1194        }
1195        // negative scale indicate that WebCore should reuse the current scale
1196        float scale = (float) viewWidth / w;
1197        if (mSettings.getUseWideViewPort()
1198                && (w < mViewportWidth || mViewportWidth == -1)) {
1199            int width = mViewportWidth;
1200            int screenWidth = Math.max(w, MIN_SCREEN_WIDTH);
1201            if (mViewportWidth == -1) {
1202                if (mSettings.getLayoutAlgorithm() ==
1203                        WebSettings.LayoutAlgorithm.NORMAL) {
1204                    width = WebView.ZOOM_OUT_WIDTH;
1205                } else {
1206                    /*
1207                     * if a page's minimum preferred width is wider than the
1208                     * given "w", use it instead to get better layout result. If
1209                     * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1210                     * wider. If we start a page with screen width, due to the
1211                     * delay between {@link #didFirstLayout} and
1212                     * {@link #viewSizeChanged},
1213                     * {@link #nativeGetContentMinPrefWidth} will return a more
1214                     * accurate value than initial 0 to result a better layout.
1215                     * In the worse case, the native width will be adjusted when
1216                     * next zoom or screen orientation change happens.
1217                     */
1218                    int minContentWidth = nativeGetContentMinPrefWidth();
1219                    if (minContentWidth > WebView.MAX_FLOAT_CONTENT_WIDTH) {
1220                        // keep the same width and screen width so that there is
1221                        // no reflow when zoom-out
1222                        width = minContentWidth;
1223                        screenWidth = Math.min(screenWidth, Math.abs(viewWidth));
1224                    } else {
1225                        width = Math.max(w, minContentWidth);
1226                    }
1227                }
1228            }
1229            nativeSetSize(width, Math.round((float) width * h / w),
1230                    screenWidth, scale, w, h);
1231        } else {
1232            nativeSetSize(w, h, w, scale, w, h);
1233        }
1234        // Remember the current width and height
1235        boolean needInvalidate = (mCurrentViewWidth == 0);
1236        mCurrentViewWidth = w;
1237        mCurrentViewHeight = h;
1238        if (needInvalidate) {
1239            // ensure {@link #webkitDraw} is called as we were blocking in
1240            // {@link #contentDraw} when mCurrentViewWidth is 0
1241            if (LOGV_ENABLED) Log.v(LOGTAG, "viewSizeChanged");
1242            contentDraw();
1243        }
1244        mEventHub.sendMessage(Message.obtain(null,
1245                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1246    }
1247
1248    private void sendUpdateTextEntry() {
1249        if (mWebView != null) {
1250            Message.obtain(mWebView.mPrivateHandler,
1251                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1252        }
1253    }
1254
1255    // Used to avoid posting more than one draw message.
1256    private boolean mDrawIsScheduled;
1257
1258    // Used to avoid posting more than one split picture message.
1259    private boolean mSplitPictureIsScheduled;
1260
1261    // Used to suspend drawing.
1262    private boolean mDrawIsPaused;
1263
1264    // Used to end scale+scroll mode, accessed by both threads
1265    boolean mEndScaleZoom = false;
1266
1267    public class DrawData {
1268        public DrawData() {
1269            mInvalRegion = new Region();
1270            mWidthHeight = new Point();
1271        }
1272        public Region mInvalRegion;
1273        public Point mViewPoint;
1274        public Point mWidthHeight;
1275    }
1276
1277    private void webkitDraw() {
1278        mDrawIsScheduled = false;
1279        DrawData draw = new DrawData();
1280        if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw start");
1281        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
1282                == false) {
1283            if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw abort");
1284            return;
1285        }
1286        if (mWebView != null) {
1287            // Send the native view size that was used during the most recent
1288            // layout.
1289            draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1290            if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1291            Message.obtain(mWebView.mPrivateHandler,
1292                    WebView.NEW_PICTURE_MSG_ID,
1293                    mViewportMinimumScale == 0 ? nativeGetContentMinPrefWidth()
1294                            : 0,
1295                    0, draw).sendToTarget();
1296            if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1297                // as we have the new picture, try to sync the scroll position
1298                Message.obtain(mWebView.mPrivateHandler,
1299                        WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1300                        mWebkitScrollY).sendToTarget();
1301                mWebkitScrollX = mWebkitScrollY = 0;
1302            }
1303            // nativeSnapToAnchor() needs to be called after NEW_PICTURE_MSG_ID
1304            // is sent, so that scroll will be based on the new content size.
1305            nativeSnapToAnchor();
1306        }
1307    }
1308
1309    ///////////////////////////////////////////////////////////////////////////
1310    // These are called from the UI thread, not our thread
1311
1312    static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
1313                                         Paint.DITHER_FLAG |
1314                                         Paint.SUBPIXEL_TEXT_FLAG;
1315    static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
1316                                           Paint.DITHER_FLAG;
1317
1318    final DrawFilter mZoomFilter =
1319                    new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
1320    final DrawFilter mScrollFilter =
1321                    new PaintFlagsDrawFilter(SCROLL_BITS, 0);
1322
1323    /* package */ void drawContentPicture(Canvas canvas, int color,
1324                                          boolean animatingZoom,
1325                                          boolean animatingScroll) {
1326        DrawFilter df = null;
1327        if (animatingZoom) {
1328            df = mZoomFilter;
1329        } else if (animatingScroll) {
1330            df = mScrollFilter;
1331        }
1332        canvas.setDrawFilter(df);
1333        boolean tookTooLong = nativeDrawContent(canvas, color);
1334        canvas.setDrawFilter(null);
1335        if (tookTooLong && mSplitPictureIsScheduled == false) {
1336            mSplitPictureIsScheduled = true;
1337            sendMessage(EventHub.SPLIT_PICTURE_SET);
1338        }
1339    }
1340
1341    /*package*/ Picture copyContentPicture() {
1342        Picture result = new Picture();
1343        nativeCopyContentToPicture(result);
1344        return result;
1345    }
1346
1347    static void pauseUpdate(WebViewCore core) {
1348        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1349        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1350        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1351        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1352                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1353        // Note: there is one possible failure mode. If pauseUpdate() is called
1354        // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
1355        // of the queue and about to be executed. mDrawIsScheduled may be set to
1356        // false in webkitDraw(). So update won't be blocked. But at least the
1357        // webcore thread priority is still lowered.
1358        if (core != null) {
1359            synchronized (core) {
1360                core.mDrawIsPaused = true;
1361                core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1362            }
1363        }
1364    }
1365
1366    static void resumeUpdate(WebViewCore core) {
1367        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1368        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1369        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1370        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1371                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1372        if (core != null) {
1373            synchronized (core) {
1374                core.mDrawIsScheduled = false;
1375                core.mDrawIsPaused = false;
1376                if (LOGV_ENABLED) Log.v(LOGTAG, "resumeUpdate");
1377                core.contentDraw();
1378            }
1379        }
1380    }
1381
1382    static void startCacheTransaction() {
1383        sWebCoreHandler.sendMessage(sWebCoreHandler
1384                .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
1385    }
1386
1387    static void endCacheTransaction() {
1388        sWebCoreHandler.sendMessage(sWebCoreHandler
1389                .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
1390    }
1391
1392    //////////////////////////////////////////////////////////////////////////
1393
1394    private void restoreState(int index) {
1395        WebBackForwardList list = mCallbackProxy.getBackForwardList();
1396        int size = list.getSize();
1397        for (int i = 0; i < size; i++) {
1398            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1399        }
1400        mBrowserFrame.mLoadInitFromJava = true;
1401        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1402        mBrowserFrame.mLoadInitFromJava = false;
1403    }
1404
1405    //-------------------------------------------------------------------------
1406    // Implement abstract methods in WebViewCore, native WebKit callback part
1407    //-------------------------------------------------------------------------
1408
1409    // called from JNI or WebView thread
1410    /* package */ void contentDraw() {
1411        // don't update the Picture until we have an initial width and finish
1412        // the first layout
1413        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1414            return;
1415        }
1416        // only fire an event if this is our first request
1417        synchronized (this) {
1418            if (mDrawIsPaused || mDrawIsScheduled) {
1419                return;
1420            }
1421            mDrawIsScheduled = true;
1422            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1423        }
1424    }
1425
1426    // called by JNI
1427    private void contentScrollBy(int dx, int dy, boolean animate) {
1428        if (!mBrowserFrame.firstLayoutDone()) {
1429            // Will this happen? If yes, we need to do something here.
1430            return;
1431        }
1432        if (mWebView != null) {
1433            Message.obtain(mWebView.mPrivateHandler,
1434                    WebView.SCROLL_BY_MSG_ID, dx, dy,
1435                    new Boolean(animate)).sendToTarget();
1436        }
1437    }
1438
1439    // called by JNI
1440    private void contentScrollTo(int x, int y) {
1441        if (!mBrowserFrame.firstLayoutDone()) {
1442            /*
1443             * WebKit restore state will be called before didFirstLayout(),
1444             * remember the position as it has to be applied after restoring
1445             * zoom factor which is controlled by screenWidth.
1446             */
1447            mRestoredX = x;
1448            mRestoredY = y;
1449            return;
1450        }
1451        if (mWebView != null) {
1452            Message.obtain(mWebView.mPrivateHandler,
1453                    WebView.SCROLL_TO_MSG_ID, x, y).sendToTarget();
1454        }
1455    }
1456
1457    // called by JNI
1458    private void contentSpawnScrollTo(int x, int y) {
1459        if (!mBrowserFrame.firstLayoutDone()) {
1460            /*
1461             * WebKit restore state will be called before didFirstLayout(),
1462             * remember the position as it has to be applied after restoring
1463             * zoom factor which is controlled by screenWidth.
1464             */
1465            mRestoredX = x;
1466            mRestoredY = y;
1467            return;
1468        }
1469        if (mWebView != null) {
1470            Message.obtain(mWebView.mPrivateHandler,
1471                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y).sendToTarget();
1472        }
1473    }
1474
1475    // called by JNI
1476    private void sendMarkNodeInvalid(int node) {
1477        if (mWebView != null) {
1478            Message.obtain(mWebView.mPrivateHandler,
1479                    WebView.MARK_NODE_INVALID_ID, node, 0).sendToTarget();
1480        }
1481    }
1482
1483    // called by JNI
1484    private void sendNotifyFocusSet() {
1485        if (mWebView != null) {
1486            Message.obtain(mWebView.mPrivateHandler,
1487                    WebView.NOTIFY_FOCUS_SET_MSG_ID).sendToTarget();
1488        }
1489    }
1490
1491    // called by JNI
1492    private void sendNotifyProgressFinished() {
1493        sendUpdateTextEntry();
1494        // as CacheManager can behave based on database transaction, we need to
1495        // call tick() to trigger endTransaction
1496        sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
1497        sWebCoreHandler.sendMessage(sWebCoreHandler
1498                .obtainMessage(WebCoreThread.CACHE_TICKER));
1499        contentDraw();
1500    }
1501
1502    // called by JNI
1503    private void sendRecomputeFocus() {
1504        if (mWebView != null) {
1505            Message.obtain(mWebView.mPrivateHandler,
1506                    WebView.RECOMPUTE_FOCUS_MSG_ID).sendToTarget();
1507        }
1508    }
1509
1510    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
1511        be scaled before they can be used by the view system, which happens
1512        in WebView since it (and its thread) know the current scale factor.
1513     */
1514    private void sendViewInvalidate(int left, int top, int right, int bottom) {
1515        if (mWebView != null) {
1516            Message.obtain(mWebView.mPrivateHandler,
1517                           WebView.INVAL_RECT_MSG_ID,
1518                           new Rect(left, top, right, bottom)).sendToTarget();
1519        }
1520    }
1521
1522    /* package */ WebView getWebView() {
1523        return mWebView;
1524    }
1525
1526    private native void setViewportSettingsFromNative();
1527
1528    // called by JNI
1529    private void didFirstLayout() {
1530        // Trick to ensure that the Picture has the exact height for the content
1531        // by forcing to layout with 0 height after the page is ready, which is
1532        // indicated by didFirstLayout. This is essential to get rid of the
1533        // white space in the GMail which uses WebView for message view.
1534        if (mWebView != null && mWebView.mHeightCanMeasure) {
1535            mWebView.mLastHeightSent = 0;
1536            // Send a negative screen width to indicate that WebCore should
1537            // reuse the current scale
1538            mEventHub.sendMessage(Message.obtain(null,
1539                    EventHub.VIEW_SIZE_CHANGED, mWebView.mLastWidthSent,
1540                    mWebView.mLastHeightSent, -mWebView.mLastWidthSent));
1541        }
1542
1543        mBrowserFrame.didFirstLayout();
1544
1545        // reset the scroll position as it is a new page now
1546        mWebkitScrollX = mWebkitScrollY = 0;
1547
1548        // set the viewport settings from WebKit
1549        setViewportSettingsFromNative();
1550
1551        // infer the values if they are not defined.
1552        if (mViewportWidth == 0) {
1553            if (mViewportInitialScale == 0) {
1554                mViewportInitialScale = 100;
1555            }
1556            if (mViewportMinimumScale == 0) {
1557                mViewportMinimumScale = 100;
1558            }
1559        }
1560        if (mViewportUserScalable == false) {
1561            mViewportInitialScale = 100;
1562            mViewportMinimumScale = 100;
1563            mViewportMaximumScale = 100;
1564        }
1565        if (mViewportMinimumScale > mViewportInitialScale) {
1566            if (mViewportInitialScale == 0) {
1567                mViewportInitialScale = mViewportMinimumScale;
1568            } else {
1569                mViewportMinimumScale = mViewportInitialScale;
1570            }
1571        }
1572        if (mViewportMaximumScale > 0) {
1573            if (mViewportMaximumScale < mViewportInitialScale) {
1574                mViewportMaximumScale = mViewportInitialScale;
1575            } else if (mViewportInitialScale == 0) {
1576                mViewportInitialScale = mViewportMaximumScale;
1577            }
1578        }
1579        if (mViewportWidth < 0 && mViewportInitialScale == 100) {
1580            mViewportWidth = 0;
1581        }
1582
1583        // now notify webview
1584        if (mWebView != null) {
1585            HashMap scaleLimit = new HashMap();
1586            scaleLimit.put("minScale", mViewportMinimumScale);
1587            scaleLimit.put("maxScale", mViewportMaximumScale);
1588
1589            if (mRestoredScale > 0) {
1590                Message.obtain(mWebView.mPrivateHandler,
1591                        WebView.DID_FIRST_LAYOUT_MSG_ID, mRestoredScale, 0,
1592                        scaleLimit).sendToTarget();
1593                mRestoredScale = 0;
1594            } else {
1595                Message.obtain(mWebView.mPrivateHandler,
1596                        WebView.DID_FIRST_LAYOUT_MSG_ID, mViewportInitialScale,
1597                        mViewportWidth, scaleLimit).sendToTarget();
1598            }
1599
1600            // if no restored offset, move the new page to (0, 0)
1601            Message.obtain(mWebView.mPrivateHandler, WebView.SCROLL_TO_MSG_ID,
1602                    mRestoredX, mRestoredY).sendToTarget();
1603            mRestoredX = mRestoredY = 0;
1604
1605            // force an early draw for quick feedback after the first layout
1606            if (mCurrentViewWidth != 0) {
1607                synchronized (this) {
1608                    if (mDrawIsScheduled) {
1609                        mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1610                    }
1611                    mDrawIsScheduled = true;
1612                    mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
1613                            EventHub.WEBKIT_DRAW));
1614                }
1615            }
1616        }
1617    }
1618
1619    // called by JNI
1620    private void restoreScale(int scale) {
1621        if (mBrowserFrame.firstLayoutDone() == false) {
1622            mRestoredScale = scale;
1623        }
1624    }
1625
1626    // called by JNI
1627    private void needTouchEvents(boolean need) {
1628        if (mWebView != null) {
1629            Message.obtain(mWebView.mPrivateHandler,
1630                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
1631                    .sendToTarget();
1632        }
1633    }
1634
1635    // called by JNI
1636    private void updateTextfield(int ptr, boolean changeToPassword,
1637            String text, int textGeneration) {
1638        if (mWebView != null) {
1639            Message msg = Message.obtain(mWebView.mPrivateHandler,
1640                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
1641                    textGeneration, text);
1642            msg.getData().putBoolean("password", changeToPassword);
1643            msg.sendToTarget();
1644        }
1645    }
1646
1647    // these must be in document space (i.e. not scaled/zoomed).
1648    private native void nativeSetScrollOffset(int dx, int dy);
1649
1650    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
1651
1652    // called by JNI
1653    private void requestListBox(String[] array, boolean[] enabledArray,
1654            int[] selectedArray) {
1655        if (mWebView != null) {
1656            mWebView.requestListBox(array, enabledArray, selectedArray);
1657        }
1658    }
1659
1660    // called by JNI
1661    private void requestListBox(String[] array, boolean[] enabledArray,
1662            int selection) {
1663        if (mWebView != null) {
1664            mWebView.requestListBox(array, enabledArray, selection);
1665        }
1666
1667    }
1668}
1669