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