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