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