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