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