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