WebViewCore.java revision 0b3a5d65247be1fb79d66af534fa78a94743864f
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;
37import android.view.View;
38
39import java.util.ArrayList;
40import java.util.Map;
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                            synchronized (WebViewCore.this) {
842                                mBrowserFrame.destroy();
843                                mBrowserFrame = null;
844                                mNativeClass = 0;
845                            }
846                            break;
847
848                        case UPDATE_FRAME_CACHE_IF_LOADING:
849                            nativeUpdateFrameCacheIfLoading();
850                            break;
851
852                        case SCROLL_TEXT_INPUT:
853                            nativeScrollFocusedTextInput(msg.arg1, msg.arg2);
854                            break;
855
856                        case LOAD_URL:
857                            loadUrl((String) msg.obj);
858                            break;
859
860                        case POST_URL: {
861                            PostUrlData param = (PostUrlData) msg.obj;
862                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
863                            break;
864                        }
865                        case LOAD_DATA:
866                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
867                            String baseUrl = loadParams.mBaseUrl;
868                            if (baseUrl != null) {
869                                int i = baseUrl.indexOf(':');
870                                if (i > 0) {
871                                    /*
872                                     * In 1.0, {@link
873                                     * WebView#loadDataWithBaseURL} can access
874                                     * local asset files as long as the data is
875                                     * valid. In the new WebKit, the restriction
876                                     * is tightened. To be compatible with 1.0,
877                                     * we automatically add the scheme of the
878                                     * baseUrl for local access as long as it is
879                                     * not http(s)/ftp(s)/about/javascript
880                                     */
881                                    String scheme = baseUrl.substring(0, i);
882                                    if (!scheme.startsWith("http") &&
883                                            !scheme.startsWith("ftp") &&
884                                            !scheme.startsWith("about") &&
885                                            !scheme.startsWith("javascript")) {
886                                        nativeRegisterURLSchemeAsLocal(scheme);
887                                    }
888                                }
889                            }
890                            mBrowserFrame.loadData(baseUrl,
891                                    loadParams.mData,
892                                    loadParams.mMimeType,
893                                    loadParams.mEncoding,
894                                    loadParams.mFailUrl);
895                            break;
896
897                        case STOP_LOADING:
898                            // If the WebCore has committed the load, but not
899                            // finished the first layout yet, we need to set
900                            // first layout done to trigger the interpreted side sync
901                            // up with native side
902                            if (mBrowserFrame.committed()
903                                    && !mBrowserFrame.firstLayoutDone()) {
904                                mBrowserFrame.didFirstLayout();
905                            }
906                            // Do this after syncing up the layout state.
907                            stopLoading();
908                            break;
909
910                        case RELOAD:
911                            mBrowserFrame.reload(false);
912                            break;
913
914                        case KEY_DOWN:
915                            key((KeyEvent) msg.obj, true);
916                            break;
917
918                        case KEY_UP:
919                            key((KeyEvent) msg.obj, false);
920                            break;
921
922                        case CLICK:
923                            nativeClick(msg.arg1, msg.arg2);
924                            break;
925
926                        case VIEW_SIZE_CHANGED: {
927                            WebView.ViewSizeData data =
928                                    (WebView.ViewSizeData) msg.obj;
929                            viewSizeChanged(data.mWidth, data.mHeight,
930                                    data.mTextWrapWidth, data.mScale,
931                                    data.mIgnoreHeight);
932                            break;
933                        }
934                        case SET_SCROLL_OFFSET:
935                            // note: these are in document coordinates
936                            // (inv-zoom)
937                            Point pt = (Point) msg.obj;
938                            nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
939                            break;
940
941                        case SET_GLOBAL_BOUNDS:
942                            Rect r = (Rect) msg.obj;
943                            nativeSetGlobalBounds(r.left, r.top, r.width(),
944                                r.height());
945                            break;
946
947                        case GO_BACK_FORWARD:
948                            // If it is a standard load and the load is not
949                            // committed yet, we interpret BACK as RELOAD
950                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
951                                    (mBrowserFrame.loadType() ==
952                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
953                                mBrowserFrame.reload(true);
954                            } else {
955                                mBrowserFrame.goBackOrForward(msg.arg1);
956                            }
957                            break;
958
959                        case RESTORE_STATE:
960                            stopLoading();
961                            restoreState(msg.arg1);
962                            break;
963
964                        case PAUSE_TIMERS:
965                            mSavedPriority = Process.getThreadPriority(mTid);
966                            Process.setThreadPriority(mTid,
967                                    Process.THREAD_PRIORITY_BACKGROUND);
968                            pauseTimers();
969                            if (CacheManager.disableTransaction()) {
970                                WebCoreThread.mCacheTickersBlocked = true;
971                                sWebCoreHandler.removeMessages(
972                                        WebCoreThread.CACHE_TICKER);
973                            }
974                            break;
975
976                        case RESUME_TIMERS:
977                            Process.setThreadPriority(mTid, mSavedPriority);
978                            resumeTimers();
979                            if (CacheManager.enableTransaction()) {
980                                WebCoreThread.mCacheTickersBlocked = false;
981                                sWebCoreHandler.sendMessageDelayed(
982                                        sWebCoreHandler.obtainMessage(
983                                        WebCoreThread.CACHE_TICKER),
984                                        WebCoreThread.CACHE_TICKER_INTERVAL);
985                            }
986                            break;
987
988                        case ON_PAUSE:
989                            nativePause();
990                            break;
991
992                        case ON_RESUME:
993                            nativeResume();
994                            break;
995
996                        case FREE_MEMORY:
997                            clearCache(false);
998                            nativeFreeMemory();
999                            break;
1000
1001                        case PLUGIN_STATE:
1002                            PluginStateData psd = (PluginStateData) msg.obj;
1003                            nativeUpdatePluginState(psd.mFrame, psd.mNode, psd.mState);
1004                            break;
1005
1006                        case SET_NETWORK_STATE:
1007                            if (BrowserFrame.sJavaBridge == null) {
1008                                throw new IllegalStateException("No WebView " +
1009                                        "has been created in this process!");
1010                            }
1011                            BrowserFrame.sJavaBridge
1012                                    .setNetworkOnLine(msg.arg1 == 1);
1013                            break;
1014
1015                        case CLEAR_CACHE:
1016                            clearCache(msg.arg1 == 1);
1017                            break;
1018
1019                        case CLEAR_HISTORY:
1020                            mCallbackProxy.getBackForwardList().
1021                                    close(mBrowserFrame.mNativeFrame);
1022                            break;
1023
1024                        case REPLACE_TEXT:
1025                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
1026                            nativeReplaceTextfieldText(msg.arg1, msg.arg2,
1027                                    rep.mReplace, rep.mNewStart, rep.mNewEnd,
1028                                    rep.mTextGeneration);
1029                            break;
1030
1031                        case PASS_TO_JS: {
1032                            JSKeyData jsData = (JSKeyData) msg.obj;
1033                            KeyEvent evt = jsData.mEvent;
1034                            int keyCode = evt.getKeyCode();
1035                            int keyValue = evt.getUnicodeChar();
1036                            int generation = msg.arg1;
1037                            passToJs(generation,
1038                                    jsData.mCurrentText,
1039                                    keyCode,
1040                                    keyValue,
1041                                    evt.isDown(),
1042                                    evt.isShiftPressed(), evt.isAltPressed(),
1043                                    evt.isSymPressed());
1044                            break;
1045                        }
1046
1047                        case SAVE_DOCUMENT_STATE: {
1048                            CursorData cDat = (CursorData) msg.obj;
1049                            nativeSaveDocumentState(cDat.mFrame);
1050                            break;
1051                        }
1052
1053                        case CLEAR_SSL_PREF_TABLE:
1054                            Network.getInstance(mContext)
1055                                    .clearUserSslPrefTable();
1056                            break;
1057
1058                        case TOUCH_UP:
1059                            TouchUpData touchUpData = (TouchUpData) msg.obj;
1060                            nativeTouchUp(touchUpData.mMoveGeneration,
1061                                    touchUpData.mFrame, touchUpData.mNode,
1062                                    touchUpData.mX, touchUpData.mY);
1063                            break;
1064
1065                        case TOUCH_EVENT: {
1066                            TouchEventData ted = (TouchEventData) msg.obj;
1067                            Message.obtain(
1068                                    mWebView.mPrivateHandler,
1069                                    WebView.PREVENT_TOUCH_ID, ted.mAction,
1070                                    nativeHandleTouchEvent(ted.mAction, ted.mX,
1071                                            ted.mY) ? 1 : 0).sendToTarget();
1072                            break;
1073                        }
1074
1075                        case SET_ACTIVE:
1076                            nativeSetFocusControllerActive(msg.arg1 == 1);
1077                            break;
1078
1079                        case ADD_JS_INTERFACE:
1080                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
1081                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
1082                                    jsData.mInterfaceName);
1083                            break;
1084
1085                        case REQUEST_EXT_REPRESENTATION:
1086                            mBrowserFrame.externalRepresentation(
1087                                    (Message) msg.obj);
1088                            break;
1089
1090                        case REQUEST_DOC_AS_TEXT:
1091                            mBrowserFrame.documentAsText((Message) msg.obj);
1092                            break;
1093
1094                        case SET_MOVE_MOUSE:
1095                            CursorData cursorData = (CursorData) msg.obj;
1096                            nativeMoveMouse(cursorData.mFrame,
1097                                     cursorData.mX, cursorData.mY);
1098                            break;
1099
1100                        case SET_MOVE_MOUSE_IF_LATEST:
1101                            CursorData cData = (CursorData) msg.obj;
1102                            nativeMoveMouseIfLatest(cData.mMoveGeneration,
1103                                    cData.mFrame,
1104                                    cData.mX, cData.mY);
1105                            break;
1106
1107                        case REQUEST_CURSOR_HREF: {
1108                            Message hrefMsg = (Message) msg.obj;
1109                            String res = nativeRetrieveHref(msg.arg1, msg.arg2);
1110                            hrefMsg.getData().putString("url", res);
1111                            hrefMsg.sendToTarget();
1112                            break;
1113                        }
1114
1115                        case UPDATE_CACHE_AND_TEXT_ENTRY:
1116                            nativeUpdateFrameCache();
1117                            // FIXME: this should provide a minimal rectangle
1118                            if (mWebView != null) {
1119                                mWebView.postInvalidate();
1120                            }
1121                            sendUpdateTextEntry();
1122                            break;
1123
1124                        case DOC_HAS_IMAGES:
1125                            Message imageResult = (Message) msg.obj;
1126                            imageResult.arg1 =
1127                                    mBrowserFrame.documentHasImages() ? 1 : 0;
1128                            imageResult.sendToTarget();
1129                            break;
1130
1131                        case DELETE_SELECTION:
1132                            TextSelectionData deleteSelectionData
1133                                    = (TextSelectionData) msg.obj;
1134                            nativeDeleteSelection(deleteSelectionData.mStart,
1135                                    deleteSelectionData.mEnd, msg.arg1);
1136                            break;
1137
1138                        case SET_SELECTION:
1139                            nativeSetSelection(msg.arg1, msg.arg2);
1140                            break;
1141
1142                        case LISTBOX_CHOICES:
1143                            SparseBooleanArray choices = (SparseBooleanArray)
1144                                    msg.obj;
1145                            int choicesSize = msg.arg1;
1146                            boolean[] choicesArray = new boolean[choicesSize];
1147                            for (int c = 0; c < choicesSize; c++) {
1148                                choicesArray[c] = choices.get(c);
1149                            }
1150                            nativeSendListBoxChoices(choicesArray,
1151                                    choicesSize);
1152                            break;
1153
1154                        case SINGLE_LISTBOX_CHOICE:
1155                            nativeSendListBoxChoice(msg.arg1);
1156                            break;
1157
1158                        case SET_BACKGROUND_COLOR:
1159                            nativeSetBackgroundColor(msg.arg1);
1160                            break;
1161
1162                        case GET_SELECTION:
1163                            String str = nativeGetSelection((Region) msg.obj);
1164                            Message.obtain(mWebView.mPrivateHandler
1165                                    , WebView.UPDATE_CLIPBOARD, str)
1166                                    .sendToTarget();
1167                            break;
1168
1169                        case DUMP_DOMTREE:
1170                            nativeDumpDomTree(msg.arg1 == 1);
1171                            break;
1172
1173                        case DUMP_RENDERTREE:
1174                            nativeDumpRenderTree(msg.arg1 == 1);
1175                            break;
1176
1177                        case DUMP_NAVTREE:
1178                            nativeDumpNavTree();
1179                            break;
1180
1181                        case SET_JS_FLAGS:
1182                            nativeSetJsFlags((String)msg.obj);
1183                            break;
1184
1185                        case GEOLOCATION_PERMISSIONS_PROVIDE:
1186                            GeolocationPermissionsData data =
1187                                    (GeolocationPermissionsData) msg.obj;
1188                            nativeGeolocationPermissionsProvide(data.mOrigin,
1189                                    data.mAllow, data.mRemember);
1190                            break;
1191
1192                        case SYNC_SCROLL:
1193                            mWebkitScrollX = msg.arg1;
1194                            mWebkitScrollY = msg.arg2;
1195                            break;
1196
1197                        case SPLIT_PICTURE_SET:
1198                            nativeSplitContent();
1199                            mSplitPictureIsScheduled = false;
1200                            break;
1201
1202                        case CLEAR_CONTENT:
1203                            // Clear the view so that onDraw() will draw nothing
1204                            // but white background
1205                            // (See public method WebView.clearView)
1206                            nativeClearContent();
1207                            break;
1208
1209                        case MESSAGE_RELAY:
1210                            if (msg.obj instanceof Message) {
1211                                ((Message) msg.obj).sendToTarget();
1212                            }
1213                            break;
1214                    }
1215                }
1216            };
1217            // Take all queued messages and resend them to the new handler.
1218            synchronized (this) {
1219                int size = mMessages.size();
1220                for (int i = 0; i < size; i++) {
1221                    mHandler.sendMessage(mMessages.get(i));
1222                }
1223                mMessages = null;
1224            }
1225        }
1226
1227        /**
1228         * Send a message internally to the queue or to the handler
1229         */
1230        private synchronized void sendMessage(Message msg) {
1231            if (mBlockMessages) {
1232                return;
1233            }
1234            if (mMessages != null) {
1235                mMessages.add(msg);
1236            } else {
1237                mHandler.sendMessage(msg);
1238            }
1239        }
1240
1241        private synchronized void removeMessages(int what) {
1242            if (mBlockMessages) {
1243                return;
1244            }
1245            if (what == EventHub.WEBKIT_DRAW) {
1246                mDrawIsScheduled = false;
1247            }
1248            if (mMessages != null) {
1249                Log.w(LOGTAG, "Not supported in this case.");
1250            } else {
1251                mHandler.removeMessages(what);
1252            }
1253        }
1254
1255        private synchronized boolean hasMessages(int what) {
1256            if (mBlockMessages) {
1257                return false;
1258            }
1259            if (mMessages != null) {
1260                Log.w(LOGTAG, "hasMessages() is not supported in this case.");
1261                return false;
1262            } else {
1263                return mHandler.hasMessages(what);
1264            }
1265        }
1266
1267        private synchronized void sendMessageDelayed(Message msg, long delay) {
1268            if (mBlockMessages) {
1269                return;
1270            }
1271            mHandler.sendMessageDelayed(msg, delay);
1272        }
1273
1274        /**
1275         * Send a message internally to the front of the queue.
1276         */
1277        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1278            if (mBlockMessages) {
1279                return;
1280            }
1281            if (mMessages != null) {
1282                mMessages.add(0, msg);
1283            } else {
1284                mHandler.sendMessageAtFrontOfQueue(msg);
1285            }
1286        }
1287
1288        /**
1289         * Remove all the messages.
1290         */
1291        private synchronized void removeMessages() {
1292            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1293            mDrawIsScheduled = false;
1294            mSplitPictureIsScheduled = false;
1295            if (mMessages != null) {
1296                mMessages.clear();
1297            } else {
1298                mHandler.removeCallbacksAndMessages(null);
1299            }
1300        }
1301
1302        /**
1303         * Block sending messages to the EventHub.
1304         */
1305        private synchronized void blockMessages() {
1306            mBlockMessages = true;
1307        }
1308    }
1309
1310    //-------------------------------------------------------------------------
1311    // Methods called by host activity (in the same thread)
1312    //-------------------------------------------------------------------------
1313
1314    void stopLoading() {
1315        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1316        if (mBrowserFrame != null) {
1317            mBrowserFrame.stopLoading();
1318        }
1319    }
1320
1321    //-------------------------------------------------------------------------
1322    // Methods called by WebView
1323    // If it refers to local variable, it needs synchronized().
1324    // If it needs WebCore, it has to send message.
1325    //-------------------------------------------------------------------------
1326
1327    void sendMessage(Message msg) {
1328        mEventHub.sendMessage(msg);
1329    }
1330
1331    void sendMessage(int what) {
1332        mEventHub.sendMessage(Message.obtain(null, what));
1333    }
1334
1335    void sendMessage(int what, Object obj) {
1336        mEventHub.sendMessage(Message.obtain(null, what, obj));
1337    }
1338
1339    void sendMessage(int what, int arg1) {
1340        // just ignore the second argument (make it 0)
1341        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1342    }
1343
1344    void sendMessage(int what, int arg1, int arg2) {
1345        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1346    }
1347
1348    void sendMessage(int what, int arg1, Object obj) {
1349        // just ignore the second argument (make it 0)
1350        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1351    }
1352
1353    void sendMessage(int what, int arg1, int arg2, Object obj) {
1354        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1355    }
1356
1357    void sendMessageDelayed(int what, Object obj, long delay) {
1358        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1359    }
1360
1361    void removeMessages(int what) {
1362        mEventHub.removeMessages(what);
1363    }
1364
1365    void removeMessages() {
1366        mEventHub.removeMessages();
1367    }
1368
1369    /**
1370     * Removes pending messages and trigger a DESTROY message to send to
1371     * WebCore.
1372     * Called from UI thread.
1373     */
1374    void destroy() {
1375        // We don't want anyone to post a message between removing pending
1376        // messages and sending the destroy message.
1377        synchronized (mEventHub) {
1378            // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
1379            // be preserved even the WebView is destroyed.
1380            // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
1381            boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
1382            boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
1383            mEventHub.removeMessages();
1384            mEventHub.sendMessageAtFrontOfQueue(
1385                    Message.obtain(null, EventHub.DESTROY));
1386            if (hasPause) {
1387                mEventHub.sendMessageAtFrontOfQueue(
1388                        Message.obtain(null, EventHub.PAUSE_TIMERS));
1389            }
1390            if (hasResume) {
1391                mEventHub.sendMessageAtFrontOfQueue(
1392                        Message.obtain(null, EventHub.RESUME_TIMERS));
1393            }
1394            mEventHub.blockMessages();
1395            mWebView = null;
1396        }
1397    }
1398
1399    //-------------------------------------------------------------------------
1400    // WebViewCore private methods
1401    //-------------------------------------------------------------------------
1402
1403    private void clearCache(boolean includeDiskFiles) {
1404        mBrowserFrame.clearCache();
1405        if (includeDiskFiles) {
1406            CacheManager.removeAllCacheFiles();
1407        }
1408    }
1409
1410    private void loadUrl(String url) {
1411        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1412        mBrowserFrame.loadUrl(url);
1413    }
1414
1415    private void key(KeyEvent evt, boolean isDown) {
1416        if (DebugFlags.WEB_VIEW_CORE) {
1417            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1418                    + evt);
1419        }
1420        int keyCode = evt.getKeyCode();
1421        if (!nativeKey(keyCode, evt.getUnicodeChar(),
1422                evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
1423                evt.isSymPressed(),
1424                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1425            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1426                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1427                if (DebugFlags.WEB_VIEW_CORE) {
1428                    Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
1429                }
1430                if (mWebView != null && evt.isDown()) {
1431                    Message.obtain(mWebView.mPrivateHandler,
1432                            WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
1433                }
1434                return;
1435            }
1436            // bubble up the event handling
1437            // but do not bubble up the ENTER key, which would open the search
1438            // bar without any text.
1439            mCallbackProxy.onUnhandledKeyEvent(evt);
1440        }
1441    }
1442
1443    // These values are used to avoid requesting a layout based on old values
1444    private int mCurrentViewWidth = 0;
1445    private int mCurrentViewHeight = 0;
1446    private float mCurrentViewScale = 1.0f;
1447
1448    // notify webkit that our virtual view size changed size (after inv-zoom)
1449    private void viewSizeChanged(int w, int h, int textwrapWidth, float scale,
1450            boolean ignoreHeight) {
1451        if (DebugFlags.WEB_VIEW_CORE) {
1452            Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
1453                    + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
1454        }
1455        if (w == 0) {
1456            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1457            return;
1458        }
1459        int width = w;
1460        if (mSettings.getUseWideViewPort()) {
1461            if (mViewportWidth == -1) {
1462                if (mSettings.getLayoutAlgorithm() ==
1463                        WebSettings.LayoutAlgorithm.NORMAL) {
1464                    width = DEFAULT_VIEWPORT_WIDTH;
1465                } else {
1466                    /*
1467                     * if a page's minimum preferred width is wider than the
1468                     * given "w", use it instead to get better layout result. If
1469                     * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1470                     * wider. If we start a page with screen width, due to the
1471                     * delay between {@link #didFirstLayout} and
1472                     * {@link #viewSizeChanged},
1473                     * {@link #nativeGetContentMinPrefWidth} will return a more
1474                     * accurate value than initial 0 to result a better layout.
1475                     * In the worse case, the native width will be adjusted when
1476                     * next zoom or screen orientation change happens.
1477                     */
1478                    width = Math.max(w, Math.max(DEFAULT_VIEWPORT_WIDTH,
1479                            nativeGetContentMinPrefWidth()));
1480                }
1481            } else {
1482                width = Math.max(w, mViewportWidth);
1483            }
1484        }
1485        nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
1486                textwrapWidth, scale, w, h, ignoreHeight);
1487        // Remember the current width and height
1488        boolean needInvalidate = (mCurrentViewWidth == 0);
1489        mCurrentViewWidth = w;
1490        mCurrentViewHeight = h;
1491        mCurrentViewScale = scale;
1492        if (needInvalidate) {
1493            // ensure {@link #webkitDraw} is called as we were blocking in
1494            // {@link #contentDraw} when mCurrentViewWidth is 0
1495            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1496            contentDraw();
1497        }
1498        mEventHub.sendMessage(Message.obtain(null,
1499                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1500    }
1501
1502    private void sendUpdateTextEntry() {
1503        if (mWebView != null) {
1504            Message.obtain(mWebView.mPrivateHandler,
1505                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1506        }
1507    }
1508
1509    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1510    // callbacks. Computes the sum of database quota for all origins.
1511    private long getUsedQuota() {
1512        WebStorage webStorage = WebStorage.getInstance();
1513        Set<String> origins = webStorage.getOrigins();
1514        if (origins == null) {
1515            return 0;
1516        }
1517        long usedQuota = 0;
1518        for (String origin : origins) {
1519            usedQuota += webStorage.getQuotaForOrigin(origin);
1520        }
1521        return usedQuota;
1522    }
1523
1524    // Used to avoid posting more than one draw message.
1525    private boolean mDrawIsScheduled;
1526
1527    // Used to avoid posting more than one split picture message.
1528    private boolean mSplitPictureIsScheduled;
1529
1530    // Used to suspend drawing.
1531    private boolean mDrawIsPaused;
1532
1533    // mRestoreState is set in didFirstLayout(), and reset in the next
1534    // webkitDraw after passing it to the UI thread.
1535    private RestoreState mRestoreState = null;
1536
1537    static class RestoreState {
1538        float mMinScale;
1539        float mMaxScale;
1540        float mViewScale;
1541        float mTextWrapScale;
1542        int mScrollX;
1543        int mScrollY;
1544        boolean mMobileSite;
1545    }
1546
1547    static class DrawData {
1548        DrawData() {
1549            mInvalRegion = new Region();
1550            mWidthHeight = new Point();
1551        }
1552        Region mInvalRegion;
1553        Point mViewPoint;
1554        Point mWidthHeight;
1555        int mMinPrefWidth;
1556        RestoreState mRestoreState; // only non-null if it is for the first
1557                                    // picture set after the first layout
1558    }
1559
1560    private void webkitDraw() {
1561        mDrawIsScheduled = false;
1562        DrawData draw = new DrawData();
1563        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
1564        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
1565                == false) {
1566            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
1567            return;
1568        }
1569        if (mWebView != null) {
1570            // Send the native view size that was used during the most recent
1571            // layout.
1572            draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1573            if (mSettings.getUseWideViewPort()) {
1574                draw.mMinPrefWidth = Math.max(
1575                        mViewportWidth == -1 ? DEFAULT_VIEWPORT_WIDTH
1576                                : (mViewportWidth == 0 ? mCurrentViewWidth
1577                                        : mViewportWidth),
1578                        nativeGetContentMinPrefWidth());
1579            }
1580            if (mRestoreState != null) {
1581                draw.mRestoreState = mRestoreState;
1582                mRestoreState = null;
1583            }
1584            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1585            Message.obtain(mWebView.mPrivateHandler,
1586                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
1587            if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1588                // as we have the new picture, try to sync the scroll position
1589                Message.obtain(mWebView.mPrivateHandler,
1590                        WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1591                        mWebkitScrollY).sendToTarget();
1592                mWebkitScrollX = mWebkitScrollY = 0;
1593            }
1594        }
1595    }
1596
1597    ///////////////////////////////////////////////////////////////////////////
1598    // These are called from the UI thread, not our thread
1599
1600    static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
1601                                         Paint.DITHER_FLAG |
1602                                         Paint.SUBPIXEL_TEXT_FLAG;
1603    static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
1604                                           Paint.DITHER_FLAG;
1605
1606    final DrawFilter mZoomFilter =
1607                    new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
1608    final DrawFilter mScrollFilter =
1609                    new PaintFlagsDrawFilter(SCROLL_BITS, 0);
1610
1611    /* package */ void drawContentPicture(Canvas canvas, int color,
1612                                          boolean animatingZoom,
1613                                          boolean animatingScroll) {
1614        DrawFilter df = null;
1615        if (animatingZoom) {
1616            df = mZoomFilter;
1617        } else if (animatingScroll) {
1618            df = mScrollFilter;
1619        }
1620        canvas.setDrawFilter(df);
1621        boolean tookTooLong = nativeDrawContent(canvas, color);
1622        canvas.setDrawFilter(null);
1623        if (tookTooLong && mSplitPictureIsScheduled == false) {
1624            mSplitPictureIsScheduled = true;
1625            sendMessage(EventHub.SPLIT_PICTURE_SET);
1626        }
1627    }
1628
1629    /* package */ synchronized boolean pictureReady() {
1630        return nativePictureReady();
1631    }
1632
1633    /*package*/ synchronized Picture copyContentPicture() {
1634        Picture result = new Picture();
1635        nativeCopyContentToPicture(result);
1636        return result;
1637    }
1638
1639    static void pauseUpdate(WebViewCore core) {
1640        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1641        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1642        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1643        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1644                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1645        // Note: there is one possible failure mode. If pauseUpdate() is called
1646        // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
1647        // of the queue and about to be executed. mDrawIsScheduled may be set to
1648        // false in webkitDraw(). So update won't be blocked. But at least the
1649        // webcore thread priority is still lowered.
1650        if (core != null) {
1651            synchronized (core) {
1652                core.mDrawIsPaused = true;
1653                core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1654            }
1655        }
1656    }
1657
1658    static void resumeUpdate(WebViewCore core) {
1659        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1660        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1661        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1662        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1663                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1664        if (core != null) {
1665            synchronized (core) {
1666                core.mDrawIsScheduled = false;
1667                core.mDrawIsPaused = false;
1668                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate");
1669                core.contentDraw();
1670            }
1671        }
1672    }
1673
1674    static void startCacheTransaction() {
1675        sWebCoreHandler.sendMessage(sWebCoreHandler
1676                .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
1677    }
1678
1679    static void endCacheTransaction() {
1680        sWebCoreHandler.sendMessage(sWebCoreHandler
1681                .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
1682    }
1683
1684    //////////////////////////////////////////////////////////////////////////
1685
1686    private void restoreState(int index) {
1687        WebBackForwardList list = mCallbackProxy.getBackForwardList();
1688        int size = list.getSize();
1689        for (int i = 0; i < size; i++) {
1690            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1691        }
1692        mBrowserFrame.mLoadInitFromJava = true;
1693        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1694        mBrowserFrame.mLoadInitFromJava = false;
1695    }
1696
1697    //-------------------------------------------------------------------------
1698    // Implement abstract methods in WebViewCore, native WebKit callback part
1699    //-------------------------------------------------------------------------
1700
1701    // called from JNI or WebView thread
1702    /* package */ void contentDraw() {
1703        // don't update the Picture until we have an initial width and finish
1704        // the first layout
1705        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1706            return;
1707        }
1708        // only fire an event if this is our first request
1709        synchronized (this) {
1710            if (mDrawIsPaused || mDrawIsScheduled) {
1711                return;
1712            }
1713            mDrawIsScheduled = true;
1714            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1715        }
1716    }
1717
1718    // called by JNI
1719    private void contentScrollBy(int dx, int dy, boolean animate) {
1720        if (!mBrowserFrame.firstLayoutDone()) {
1721            // Will this happen? If yes, we need to do something here.
1722            return;
1723        }
1724        if (mWebView != null) {
1725            Message msg = Message.obtain(mWebView.mPrivateHandler,
1726                    WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
1727            if (mDrawIsScheduled) {
1728                mEventHub.sendMessage(Message.obtain(null,
1729                        EventHub.MESSAGE_RELAY, msg));
1730            } else {
1731                msg.sendToTarget();
1732            }
1733        }
1734    }
1735
1736    // called by JNI
1737    private void contentScrollTo(int x, int y) {
1738        if (!mBrowserFrame.firstLayoutDone()) {
1739            /*
1740             * WebKit restore state will be called before didFirstLayout(),
1741             * remember the position as it has to be applied after restoring
1742             * zoom factor which is controlled by screenWidth.
1743             */
1744            mRestoredX = x;
1745            mRestoredY = y;
1746            return;
1747        }
1748        if (mWebView != null) {
1749            Message msg = Message.obtain(mWebView.mPrivateHandler,
1750                    WebView.SCROLL_TO_MSG_ID, x, y);
1751            if (mDrawIsScheduled) {
1752                mEventHub.sendMessage(Message.obtain(null,
1753                        EventHub.MESSAGE_RELAY, msg));
1754            } else {
1755                msg.sendToTarget();
1756            }
1757        }
1758    }
1759
1760    // called by JNI
1761    private void contentSpawnScrollTo(int x, int y) {
1762        if (!mBrowserFrame.firstLayoutDone()) {
1763            /*
1764             * WebKit restore state will be called before didFirstLayout(),
1765             * remember the position as it has to be applied after restoring
1766             * zoom factor which is controlled by screenWidth.
1767             */
1768            mRestoredX = x;
1769            mRestoredY = y;
1770            return;
1771        }
1772        if (mWebView != null) {
1773            Message msg = Message.obtain(mWebView.mPrivateHandler,
1774                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
1775            if (mDrawIsScheduled) {
1776                mEventHub.sendMessage(Message.obtain(null,
1777                        EventHub.MESSAGE_RELAY, msg));
1778            } else {
1779                msg.sendToTarget();
1780            }
1781        }
1782    }
1783
1784    // called by JNI
1785    private void sendNotifyProgressFinished() {
1786        sendUpdateTextEntry();
1787        // as CacheManager can behave based on database transaction, we need to
1788        // call tick() to trigger endTransaction
1789        sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
1790        sWebCoreHandler.sendMessage(sWebCoreHandler
1791                .obtainMessage(WebCoreThread.CACHE_TICKER));
1792        contentDraw();
1793    }
1794
1795    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
1796        be scaled before they can be used by the view system, which happens
1797        in WebView since it (and its thread) know the current scale factor.
1798     */
1799    private void sendViewInvalidate(int left, int top, int right, int bottom) {
1800        if (mWebView != null) {
1801            Message.obtain(mWebView.mPrivateHandler,
1802                           WebView.INVAL_RECT_MSG_ID,
1803                           new Rect(left, top, right, bottom)).sendToTarget();
1804        }
1805    }
1806
1807    /* package */ WebView getWebView() {
1808        return mWebView;
1809    }
1810
1811    private native void setViewportSettingsFromNative();
1812
1813    // called by JNI
1814    private void didFirstLayout(boolean standardLoad) {
1815        if (DebugFlags.WEB_VIEW_CORE) {
1816            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
1817        }
1818
1819        mBrowserFrame.didFirstLayout();
1820
1821        if (mWebView == null) return;
1822
1823        setupViewport(standardLoad || mRestoredScale > 0);
1824
1825        // reset the scroll position, the restored offset and scales
1826        mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY
1827                = mRestoredScale = mRestoredScreenWidthScale = 0;
1828    }
1829
1830    // called by JNI
1831    private void updateViewport() {
1832        // if updateViewport is called before first layout, wait until first
1833        // layout to update the viewport. In the rare case, this is called after
1834        // first layout, force an update as we have just parsed the viewport
1835        // meta tag.
1836        if (mBrowserFrame.firstLayoutDone()) {
1837            setupViewport(true);
1838        }
1839    }
1840
1841    private void setupViewport(boolean updateRestoreState) {
1842        // set the viewport settings from WebKit
1843        setViewportSettingsFromNative();
1844
1845        // adjust the default scale to match the density
1846        if (WebView.DEFAULT_SCALE_PERCENT != 100) {
1847            float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f;
1848            if (mViewportInitialScale > 0) {
1849                mViewportInitialScale *= adjust;
1850            }
1851            if (mViewportMinimumScale > 0) {
1852                mViewportMinimumScale *= adjust;
1853            }
1854            if (mViewportMaximumScale > 0) {
1855                mViewportMaximumScale *= adjust;
1856            }
1857        }
1858
1859        // infer the values if they are not defined.
1860        if (mViewportWidth == 0) {
1861            if (mViewportInitialScale == 0) {
1862                mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT;
1863            }
1864        }
1865        if (mViewportUserScalable == false) {
1866            mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT;
1867            mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT;
1868            mViewportMaximumScale = WebView.DEFAULT_SCALE_PERCENT;
1869        }
1870        if (mViewportMinimumScale > mViewportInitialScale) {
1871            if (mViewportInitialScale == 0) {
1872                mViewportInitialScale = mViewportMinimumScale;
1873            } else {
1874                mViewportMinimumScale = mViewportInitialScale;
1875            }
1876        }
1877        if (mViewportMaximumScale > 0) {
1878            if (mViewportMaximumScale < mViewportInitialScale) {
1879                mViewportMaximumScale = mViewportInitialScale;
1880            } else if (mViewportInitialScale == 0) {
1881                mViewportInitialScale = mViewportMaximumScale;
1882            }
1883        }
1884        if (mViewportWidth < 0
1885                && mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) {
1886            mViewportWidth = 0;
1887        }
1888
1889        // if mViewportWidth is 0, it means device-width, always update.
1890        if (mViewportWidth != 0 && !updateRestoreState) return;
1891
1892        // now notify webview
1893        int webViewWidth = Math.round(mCurrentViewWidth * mCurrentViewScale);
1894        mRestoreState = new RestoreState();
1895        mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
1896        mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
1897        mRestoreState.mScrollX = mRestoredX;
1898        mRestoreState.mScrollY = mRestoredY;
1899        mRestoreState.mMobileSite = (0 == mViewportWidth);
1900        if (mRestoredScale > 0) {
1901            if (mRestoredScreenWidthScale > 0) {
1902                mRestoreState.mTextWrapScale =
1903                        mRestoredScreenWidthScale / 100.0f;
1904                // 0 will trigger WebView to turn on zoom overview mode
1905                mRestoreState.mViewScale = 0;
1906            } else {
1907                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1908                        mRestoredScale / 100.0f;
1909            }
1910        } else {
1911            if (mViewportInitialScale > 0) {
1912                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1913                        mViewportInitialScale / 100.0f;
1914            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
1915                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1916                        (float) webViewWidth / mViewportWidth;
1917            } else {
1918                mRestoreState.mTextWrapScale =
1919                        WebView.DEFAULT_SCALE_PERCENT / 100.0f;
1920                // 0 will trigger WebView to turn on zoom overview mode
1921                mRestoreState.mViewScale = 0;
1922            }
1923        }
1924
1925        if (mWebView.mHeightCanMeasure) {
1926            // Trick to ensure that the Picture has the exact height for the
1927            // content by forcing to layout with 0 height after the page is
1928            // ready, which is indicated by didFirstLayout. This is essential to
1929            // get rid of the white space in the GMail which uses WebView for
1930            // message view.
1931            mWebView.mLastHeightSent = 0;
1932            // Send a negative scale to indicate that WebCore should reuse
1933            // the current scale
1934            WebView.ViewSizeData data = new WebView.ViewSizeData();
1935            data.mWidth = mWebView.mLastWidthSent;
1936            data.mHeight = 0;
1937            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
1938            // true. It is safe to use mWidth for mTextWrapWidth.
1939            data.mTextWrapWidth = data.mWidth;
1940            data.mScale = -1.0f;
1941            data.mIgnoreHeight = false;
1942            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
1943                    EventHub.VIEW_SIZE_CHANGED, data));
1944        } else if (mSettings.getUseWideViewPort()) {
1945            if (mCurrentViewWidth == 0) {
1946                // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
1947                // to WebViewCore
1948                mWebView.mLastWidthSent = 0;
1949            } else {
1950                WebView.ViewSizeData data = new WebView.ViewSizeData();
1951                // mViewScale as 0 means it is in zoom overview mode. So we don't
1952                // know the exact scale. If mRestoredScale is non-zero, use it;
1953                // otherwise just use mTextWrapScale as the initial scale.
1954                data.mScale = mRestoreState.mViewScale == 0
1955                        ? (mRestoredScale > 0 ? mRestoredScale
1956                                : mRestoreState.mTextWrapScale)
1957                        : mRestoreState.mViewScale;
1958                data.mWidth = Math.round(webViewWidth / data.mScale);
1959                data.mHeight = mCurrentViewHeight * data.mWidth
1960                        / mCurrentViewWidth;
1961                data.mTextWrapWidth = Math.round(webViewWidth
1962                        / mRestoreState.mTextWrapScale);
1963                data.mIgnoreHeight = false;
1964                mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
1965                        EventHub.VIEW_SIZE_CHANGED, data));
1966            }
1967        }
1968    }
1969
1970    // called by JNI
1971    private void restoreScale(int scale) {
1972        if (mBrowserFrame.firstLayoutDone() == false) {
1973            mRestoredScale = scale;
1974        }
1975    }
1976
1977    // called by JNI
1978    private void restoreScreenWidthScale(int scale) {
1979        if (!mSettings.getUseWideViewPort()) {
1980            return;
1981        }
1982
1983        if (mBrowserFrame.firstLayoutDone() == false) {
1984            mRestoredScreenWidthScale = scale;
1985        }
1986    }
1987
1988    // called by JNI
1989    private void needTouchEvents(boolean need) {
1990        if (mWebView != null) {
1991            Message.obtain(mWebView.mPrivateHandler,
1992                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
1993                    .sendToTarget();
1994        }
1995    }
1996
1997    // called by JNI
1998    private void updateTextfield(int ptr, boolean changeToPassword,
1999            String text, int textGeneration) {
2000        if (mWebView != null) {
2001            Message msg = Message.obtain(mWebView.mPrivateHandler,
2002                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
2003                    textGeneration, text);
2004            msg.getData().putBoolean("password", changeToPassword);
2005            msg.sendToTarget();
2006        }
2007    }
2008
2009    // called by JNI
2010    private void updateTextSelection(int pointer, int start, int end,
2011            int textGeneration) {
2012        if (mWebView != null) {
2013            Message.obtain(mWebView.mPrivateHandler,
2014                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
2015                new TextSelectionData(start, end)).sendToTarget();
2016        }
2017    }
2018
2019    // called by JNI
2020    private void clearTextEntry() {
2021        if (mWebView == null) return;
2022        Message.obtain(mWebView.mPrivateHandler,
2023                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
2024    }
2025
2026    private native void nativeUpdateFrameCacheIfLoading();
2027
2028    /**
2029     * Scroll the focused textfield to (x, y) in document space
2030     */
2031    private native void nativeScrollFocusedTextInput(int x, int y);
2032
2033    // these must be in document space (i.e. not scaled/zoomed).
2034    private native void nativeSetScrollOffset(int gen, int dx, int dy);
2035
2036    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
2037
2038    // called by JNI
2039    private void requestListBox(String[] array, boolean[] enabledArray,
2040            int[] selectedArray) {
2041        if (mWebView != null) {
2042            mWebView.requestListBox(array, enabledArray, selectedArray);
2043        }
2044    }
2045
2046    // called by JNI
2047    private void requestListBox(String[] array, boolean[] enabledArray,
2048            int selection) {
2049        if (mWebView != null) {
2050            mWebView.requestListBox(array, enabledArray, selection);
2051        }
2052
2053    }
2054
2055    // called by JNI
2056    private void requestKeyboard(boolean showKeyboard) {
2057        if (mWebView != null) {
2058            Message.obtain(mWebView.mPrivateHandler,
2059                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
2060                    .sendToTarget();
2061        }
2062    }
2063
2064    // PluginWidget functions for creating SurfaceViews for the Surface drawing
2065    // model.
2066    private ViewManager.ChildView createSurface(String packageName, String className,
2067            int npp, int x, int y, int width, int height) {
2068        if (mWebView == null) {
2069            return null;
2070        }
2071        PluginStub stub = PluginUtil.getPluginStub(mWebView.getContext(), packageName, className);
2072        if (stub == null) {
2073            Log.e(LOGTAG, "Unable to find plugin class (" + className +
2074                    ") in the apk (" + packageName + ")");
2075            return null;
2076        }
2077
2078        View pluginView = stub.getEmbeddedView(npp, mWebView.getContext());
2079
2080        ViewManager.ChildView view = mWebView.mViewManager.createView();
2081        view.mView = pluginView;
2082        view.attachView(x, y, width, height);
2083        return view;
2084    }
2085
2086    private void destroySurface(ViewManager.ChildView childView) {
2087        childView.removeView();
2088    }
2089
2090    private native void nativePause();
2091    private native void nativeResume();
2092    private native void nativeFreeMemory();
2093}
2094