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