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