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