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