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