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