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