WebViewCore.java revision 9e28c4ce666f1b8c5e5355d739e37480856ef509
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;
44
45import java.util.ArrayList;
46import java.util.Collection;
47import java.util.Map;
48import java.util.Set;
49
50import junit.framework.Assert;
51
52final class WebViewCore {
53
54    private static final String LOGTAG = "webcore";
55
56    static {
57        // Load libwebcore during static initialization. This happens in the
58        // zygote process so it will be shared read-only across all app
59        // processes.
60        System.loadLibrary("webcore");
61    }
62
63    /*
64     * WebViewCore always executes in the same thread as the native webkit.
65     */
66
67    // The WebView that corresponds to this WebViewCore.
68    private WebView mWebView;
69    // Proxy for handling callbacks from native code
70    private final CallbackProxy mCallbackProxy;
71    // Settings object for maintaining all settings
72    private final WebSettings mSettings;
73    // Context for initializing the BrowserFrame with the proper assets.
74    private final Context mContext;
75    // The pointer to a native view object.
76    private int mNativeClass;
77    // The BrowserFrame is an interface to the native Frame component.
78    private BrowserFrame mBrowserFrame;
79    // Custom JS interfaces to add during the initialization.
80    private Map<String, Object> mJavascriptInterfaces;
81    /*
82     * range is from 200 to 10,000. 0 is a special value means device-width. -1
83     * means undefined.
84     */
85    private int mViewportWidth = -1;
86
87    /*
88     * range is from 200 to 10,000. 0 is a special value means device-height. -1
89     * means undefined.
90     */
91    private int mViewportHeight = -1;
92
93    /*
94     * scale in percent, range is from 1 to 1000. 0 means undefined.
95     */
96    private int mViewportInitialScale = 0;
97
98    /*
99     * scale in percent, range is from 1 to 1000. 0 means undefined.
100     */
101    private int mViewportMinimumScale = 0;
102
103    /*
104     * scale in percent, range is from 1 to 1000. 0 means undefined.
105     */
106    private int mViewportMaximumScale = 0;
107
108    private boolean mViewportUserScalable = true;
109
110    /*
111     * range is from 70 to 400.
112     * 0 is a special value means device-dpi. The default scale factor will be
113     * always 100.
114     * -1 means undefined. The default scale factor will be
115     * WebView.DEFAULT_SCALE_PERCENT.
116     */
117    private int mViewportDensityDpi = -1;
118
119    private int mRestoredScale = 0;
120    private int mRestoredScreenWidthScale = 0;
121    private int mRestoredX = 0;
122    private int mRestoredY = 0;
123
124    private int mWebkitScrollX = 0;
125    private int mWebkitScrollY = 0;
126
127    // If the site doesn't use viewport meta tag to specify the viewport, use
128    // DEFAULT_VIEWPORT_WIDTH as default viewport width
129    static final int DEFAULT_VIEWPORT_WIDTH = 800;
130
131    // The thread name used to identify the WebCore thread and for use in
132    // debugging other classes that require operation within the WebCore thread.
133    /* package */ static final String THREAD_NAME = "WebViewCoreThread";
134
135    public WebViewCore(Context context, WebView w, CallbackProxy proxy,
136            Map<String, Object> javascriptInterfaces) {
137        // No need to assign this in the WebCore thread.
138        mCallbackProxy = proxy;
139        mWebView = w;
140        mJavascriptInterfaces = javascriptInterfaces;
141        // This context object is used to initialize the WebViewCore during
142        // subwindow creation.
143        mContext = context;
144
145        // We need to wait for the initial thread creation before sending
146        // a message to the WebCore thread.
147        // XXX: This is the only time the UI thread will wait for the WebCore
148        // thread!
149        synchronized (WebViewCore.class) {
150            if (sWebCoreHandler == null) {
151                // Create a global thread and start it.
152                Thread t = new Thread(new WebCoreThread());
153                t.setName(THREAD_NAME);
154                t.start();
155                try {
156                    WebViewCore.class.wait();
157                } catch (InterruptedException e) {
158                    Log.e(LOGTAG, "Caught exception while waiting for thread " +
159                           "creation.");
160                    Log.e(LOGTAG, Log.getStackTraceString(e));
161                }
162            }
163        }
164        // Create an EventHub to handle messages before and after the thread is
165        // ready.
166        mEventHub = new EventHub();
167        // Create a WebSettings object for maintaining all settings
168        mSettings = new WebSettings(mContext, mWebView);
169        // The WebIconDatabase needs to be initialized within the UI thread so
170        // just request the instance here.
171        WebIconDatabase.getInstance();
172        // Create the WebStorage singleton and the UI handler
173        WebStorage.getInstance().createUIHandler();
174        // Create the UI handler for GeolocationPermissions
175        GeolocationPermissions.getInstance().createUIHandler();
176        // Send a message to initialize the WebViewCore.
177        Message init = sWebCoreHandler.obtainMessage(
178                WebCoreThread.INITIALIZE, this);
179        sWebCoreHandler.sendMessage(init);
180    }
181
182    /* Initialize private data within the WebCore thread.
183     */
184    private void initialize() {
185        /* Initialize our private BrowserFrame class to handle all
186         * frame-related functions. We need to create a new view which
187         * in turn creates a C level FrameView and attaches it to the frame.
188         */
189        mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy,
190                mSettings, mJavascriptInterfaces);
191        mJavascriptInterfaces = null;
192        // Sync the native settings and also create the WebCore thread handler.
193        mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
194        // Create the handler and transfer messages for the IconDatabase
195        WebIconDatabase.getInstance().createHandler();
196        // Create the handler for WebStorage
197        WebStorage.getInstance().createHandler();
198        // Create the handler for GeolocationPermissions.
199        GeolocationPermissions.getInstance().createHandler();
200        // The transferMessages call will transfer all pending messages to the
201        // WebCore thread handler.
202        mEventHub.transferMessages();
203
204        // Send a message back to WebView to tell it that we have set up the
205        // WebCore thread.
206        if (mWebView != null) {
207            Message.obtain(mWebView.mPrivateHandler,
208                    WebView.WEBCORE_INITIALIZED_MSG_ID,
209                    mNativeClass, 0).sendToTarget();
210        }
211
212    }
213
214    /* Handle the initialization of WebViewCore during subwindow creation. This
215     * method is called from the WebCore thread but it is called before the
216     * INITIALIZE message can be handled.
217     */
218    /* package */ void initializeSubwindow() {
219        // Go ahead and initialize the core components.
220        initialize();
221        // Remove the INITIALIZE method so we don't try to initialize twice.
222        sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this);
223    }
224
225    /* Get the BrowserFrame component. This is used for subwindow creation and
226     * is called only from BrowserFrame in the WebCore thread. */
227    /* package */ BrowserFrame getBrowserFrame() {
228        return mBrowserFrame;
229    }
230
231    //-------------------------------------------------------------------------
232    // Common methods
233    //-------------------------------------------------------------------------
234
235    /**
236     * Causes all timers to pause. This applies to all WebViews in the current
237     * app process.
238     */
239    public static void pauseTimers() {
240        if (BrowserFrame.sJavaBridge == null) {
241            throw new IllegalStateException(
242                    "No WebView has been created in this process!");
243        }
244        BrowserFrame.sJavaBridge.pause();
245    }
246
247    /**
248     * Resume all timers. This applies to all WebViews in the current process.
249     */
250    public static void resumeTimers() {
251        if (BrowserFrame.sJavaBridge == null) {
252            throw new IllegalStateException(
253                    "No WebView has been created in this process!");
254        }
255        BrowserFrame.sJavaBridge.resume();
256    }
257
258    public WebSettings getSettings() {
259        return mSettings;
260    }
261
262    /**
263     * Add an error message to the client's console.
264     * @param message The message to add
265     * @param lineNumber the line on which the error occurred
266     * @param sourceID the filename of the source that caused the error.
267     */
268    protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
269        mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
270    }
271
272    /**
273     * Invoke a javascript alert.
274     * @param message The message displayed in the alert.
275     */
276    protected void jsAlert(String url, String message) {
277        mCallbackProxy.onJsAlert(url, message);
278    }
279
280
281    /**
282     * Called by JNI.  Open a file chooser to upload a file.
283     * @return String version of the URI plus the name of the file.
284     * FIXME: Just return the URI here, and in FileSystem::pathGetFileName, call
285     * into Java to get the filename.
286     */
287    private String openFileChooser() {
288        Uri uri = mCallbackProxy.openFileChooser();
289        if (uri == null) return "";
290        // Find out the name, and append it to the URI.
291        // Webkit will treat the name as the filename, and
292        // the URI as the path.  The URI will be used
293        // in BrowserFrame to get the actual data.
294        Cursor cursor = mContext.getContentResolver().query(
295                uri,
296                new String[] { OpenableColumns.DISPLAY_NAME },
297                null,
298                null,
299                null);
300        String name = "";
301        if (cursor != null) {
302            try {
303                if (cursor.moveToNext()) {
304                    name = cursor.getString(0);
305                }
306            } finally {
307                cursor.close();
308            }
309        }
310        return uri.toString() + "/" + name;
311    }
312
313    /**
314     * Notify the browser that the origin has exceeded it's database quota.
315     * @param url The URL that caused the overflow.
316     * @param databaseIdentifier The identifier of the database.
317     * @param currentQuota The current quota for the origin.
318     * @param estimatedSize The estimated size of the database.
319     */
320    protected void exceededDatabaseQuota(String url,
321                                         String databaseIdentifier,
322                                         long currentQuota,
323                                         long estimatedSize) {
324        // Inform the callback proxy of the quota overflow. Send an object
325        // that encapsulates a call to the nativeSetDatabaseQuota method to
326        // awaken the sleeping webcore thread when a decision from the
327        // client to allow or deny quota is available.
328        mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
329                currentQuota, estimatedSize, getUsedQuota(),
330                new WebStorage.QuotaUpdater() {
331                        public void updateQuota(long quota) {
332                            nativeSetNewStorageLimit(quota);
333                        }
334                });
335    }
336
337    /**
338     * Notify the browser that the appcache has exceeded its max size.
339     * @param spaceNeeded is the amount of disk space that would be needed
340     * in order for the last appcache operation to succeed.
341     */
342    protected void reachedMaxAppCacheSize(long spaceNeeded) {
343        mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(),
344                new WebStorage.QuotaUpdater() {
345                    public void updateQuota(long quota) {
346                        nativeSetNewStorageLimit(quota);
347                    }
348                });
349    }
350
351    protected void populateVisitedLinks() {
352        ValueCallback callback = new ValueCallback<String[]>() {
353            public void onReceiveValue(String[] value) {
354                sendMessage(EventHub.POPULATE_VISITED_LINKS, (Object)value);
355            }
356        };
357        mCallbackProxy.getVisitedHistory(callback);
358    }
359
360    /**
361     * Shows a prompt to ask the user to set the Geolocation permission state
362     * for the given origin.
363     * @param origin The origin for which Geolocation permissions are
364     *     requested.
365     */
366    protected void geolocationPermissionsShowPrompt(String origin) {
367        mCallbackProxy.onGeolocationPermissionsShowPrompt(origin,
368                new GeolocationPermissions.Callback() {
369          public void invoke(String origin, boolean allow, boolean remember) {
370            GeolocationPermissionsData data = new GeolocationPermissionsData();
371            data.mOrigin = origin;
372            data.mAllow = allow;
373            data.mRemember = remember;
374            // Marshall to WebCore thread.
375            sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data);
376          }
377        });
378    }
379
380    /**
381     * Hides the Geolocation permissions prompt.
382     */
383    protected void geolocationPermissionsHidePrompt() {
384        mCallbackProxy.onGeolocationPermissionsHidePrompt();
385    }
386
387    /**
388     * Invoke a javascript confirm dialog.
389     * @param message The message displayed in the dialog.
390     * @return True if the user confirmed or false if the user cancelled.
391     */
392    protected boolean jsConfirm(String url, String message) {
393        return mCallbackProxy.onJsConfirm(url, message);
394    }
395
396    /**
397     * Invoke a javascript prompt dialog.
398     * @param message The message to be displayed in the dialog.
399     * @param defaultValue The default value in the prompt input.
400     * @return The input from the user or null to indicate the user cancelled
401     *         the dialog.
402     */
403    protected String jsPrompt(String url, String message, String defaultValue) {
404        return mCallbackProxy.onJsPrompt(url, message, defaultValue);
405    }
406
407    /**
408     * Invoke a javascript before unload dialog.
409     * @param url The url that is requesting the dialog.
410     * @param message The message displayed in the dialog.
411     * @return True if the user confirmed or false if the user cancelled. False
412     *         will cancel the navigation.
413     */
414    protected boolean jsUnload(String url, String message) {
415        return mCallbackProxy.onJsBeforeUnload(url, message);
416    }
417
418    /**
419     *
420     * Callback to notify that a JavaScript execution timeout has occured.
421     * @return True if the JavaScript execution should be interrupted. False
422     *         will continue the execution.
423     */
424    protected boolean jsInterrupt() {
425        return mCallbackProxy.onJsTimeout();
426    }
427
428    //-------------------------------------------------------------------------
429    // JNI methods
430    //-------------------------------------------------------------------------
431
432    static native String nativeFindAddress(String addr, boolean caseInsensitive);
433
434    /**
435     * Empty the picture set.
436     */
437    private native void nativeClearContent();
438
439    /**
440     * Create a flat picture from the set of pictures.
441     */
442    private native void nativeCopyContentToPicture(Picture picture);
443
444    /**
445     * Draw the picture set with a background color. Returns true
446     * if some individual picture took too long to draw and can be
447     * split into parts. Called from the UI thread.
448     */
449    private native boolean nativeDrawContent(Canvas canvas, int color);
450
451    /**
452     * check to see if picture is blank and in progress
453     */
454    private native boolean nativePictureReady();
455
456    /**
457     * Redraw a portion of the picture set. The Point wh returns the
458     * width and height of the overall picture.
459     */
460    private native boolean nativeRecordContent(Region invalRegion, Point wh);
461
462    private native boolean nativeFocusBoundsChanged();
463
464    /**
465     * Splits slow parts of the picture set. Called from the webkit
466     * thread after nativeDrawContent returns true.
467     */
468    private native void nativeSplitContent();
469
470    private native boolean nativeKey(int keyCode, int unichar,
471            int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
472            boolean isDown);
473
474    private native void nativeClick(int framePtr, int nodePtr);
475
476    private native void nativeSendListBoxChoices(boolean[] choices, int size);
477
478    private native void nativeSendListBoxChoice(int choice);
479
480    /*  Tell webkit what its width and height are, for the purposes
481        of layout/line-breaking. These coordinates are in document space,
482        which is the same as View coords unless we have zoomed the document
483        (see nativeSetZoom).
484        screenWidth is used by layout to wrap column around. If viewport uses
485        fixed size, screenWidth can be different from width with zooming.
486        should this be called nativeSetViewPortSize?
487    */
488    private native void nativeSetSize(int width, int height, int screenWidth,
489            float scale, int realScreenWidth, int screenHeight,
490            boolean ignoreHeight);
491
492    private native int nativeGetContentMinPrefWidth();
493
494    // Start: functions that deal with text editing
495    private native void nativeReplaceTextfieldText(
496            int oldStart, int oldEnd, String replace, int newStart, int newEnd,
497            int textGeneration);
498
499    private native void passToJs(int gen,
500            String currentText, int keyCode, int keyValue, boolean down,
501            boolean cap, boolean fn, boolean sym);
502
503    private native void nativeSetFocusControllerActive(boolean active);
504
505    private native void nativeSaveDocumentState(int frame);
506
507    private native void nativeMoveMouse(int framePtr, int x, int y);
508
509    private native void nativeMoveMouseIfLatest(int moveGeneration,
510            int framePtr, int x, int y);
511
512    private native String nativeRetrieveHref(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                            String res = nativeRetrieveHref(msg.arg1, msg.arg2);
1168                            hrefMsg.getData().putString("url", res);
1169                            hrefMsg.sendToTarget();
1170                            break;
1171                        }
1172
1173                        case UPDATE_CACHE_AND_TEXT_ENTRY:
1174                            nativeUpdateFrameCache();
1175                            // FIXME: this should provide a minimal rectangle
1176                            if (mWebView != null) {
1177                                mWebView.postInvalidate();
1178                            }
1179                            sendUpdateTextEntry();
1180                            break;
1181
1182                        case DOC_HAS_IMAGES:
1183                            Message imageResult = (Message) msg.obj;
1184                            imageResult.arg1 =
1185                                    mBrowserFrame.documentHasImages() ? 1 : 0;
1186                            imageResult.sendToTarget();
1187                            break;
1188
1189                        case DELETE_SELECTION:
1190                            TextSelectionData deleteSelectionData
1191                                    = (TextSelectionData) msg.obj;
1192                            nativeDeleteSelection(deleteSelectionData.mStart,
1193                                    deleteSelectionData.mEnd, msg.arg1);
1194                            break;
1195
1196                        case SET_SELECTION:
1197                            nativeSetSelection(msg.arg1, msg.arg2);
1198                            break;
1199
1200                        case LISTBOX_CHOICES:
1201                            SparseBooleanArray choices = (SparseBooleanArray)
1202                                    msg.obj;
1203                            int choicesSize = msg.arg1;
1204                            boolean[] choicesArray = new boolean[choicesSize];
1205                            for (int c = 0; c < choicesSize; c++) {
1206                                choicesArray[c] = choices.get(c);
1207                            }
1208                            nativeSendListBoxChoices(choicesArray,
1209                                    choicesSize);
1210                            break;
1211
1212                        case SINGLE_LISTBOX_CHOICE:
1213                            nativeSendListBoxChoice(msg.arg1);
1214                            break;
1215
1216                        case SET_BACKGROUND_COLOR:
1217                            nativeSetBackgroundColor(msg.arg1);
1218                            break;
1219
1220                        case GET_SELECTION:
1221                            String str = nativeGetSelection((Region) msg.obj);
1222                            Message.obtain(mWebView.mPrivateHandler
1223                                    , WebView.UPDATE_CLIPBOARD, str)
1224                                    .sendToTarget();
1225                            break;
1226
1227                        case DUMP_DOMTREE:
1228                            nativeDumpDomTree(msg.arg1 == 1);
1229                            break;
1230
1231                        case DUMP_RENDERTREE:
1232                            nativeDumpRenderTree(msg.arg1 == 1);
1233                            break;
1234
1235                        case DUMP_NAVTREE:
1236                            nativeDumpNavTree();
1237                            break;
1238
1239                        case SET_JS_FLAGS:
1240                            nativeSetJsFlags((String)msg.obj);
1241                            break;
1242
1243                        case GEOLOCATION_PERMISSIONS_PROVIDE:
1244                            GeolocationPermissionsData data =
1245                                    (GeolocationPermissionsData) msg.obj;
1246                            nativeGeolocationPermissionsProvide(data.mOrigin,
1247                                    data.mAllow, data.mRemember);
1248                            break;
1249
1250                        case SYNC_SCROLL:
1251                            mWebkitScrollX = msg.arg1;
1252                            mWebkitScrollY = msg.arg2;
1253                            break;
1254
1255                        case SPLIT_PICTURE_SET:
1256                            nativeSplitContent();
1257                            mSplitPictureIsScheduled = false;
1258                            break;
1259
1260                        case CLEAR_CONTENT:
1261                            // Clear the view so that onDraw() will draw nothing
1262                            // but white background
1263                            // (See public method WebView.clearView)
1264                            nativeClearContent();
1265                            break;
1266
1267                        case MESSAGE_RELAY:
1268                            if (msg.obj instanceof Message) {
1269                                ((Message) msg.obj).sendToTarget();
1270                            }
1271                            break;
1272
1273                        case POPULATE_VISITED_LINKS:
1274                            nativeProvideVisitedHistory((String[])msg.obj);
1275                            break;
1276                    }
1277                }
1278            };
1279            // Take all queued messages and resend them to the new handler.
1280            synchronized (this) {
1281                int size = mMessages.size();
1282                for (int i = 0; i < size; i++) {
1283                    mHandler.sendMessage(mMessages.get(i));
1284                }
1285                mMessages = null;
1286            }
1287        }
1288
1289        /**
1290         * Send a message internally to the queue or to the handler
1291         */
1292        private synchronized void sendMessage(Message msg) {
1293            if (mBlockMessages) {
1294                return;
1295            }
1296            if (mMessages != null) {
1297                mMessages.add(msg);
1298            } else {
1299                mHandler.sendMessage(msg);
1300            }
1301        }
1302
1303        private synchronized void removeMessages(int what) {
1304            if (mBlockMessages) {
1305                return;
1306            }
1307            if (what == EventHub.WEBKIT_DRAW) {
1308                mDrawIsScheduled = false;
1309            }
1310            if (mMessages != null) {
1311                Log.w(LOGTAG, "Not supported in this case.");
1312            } else {
1313                mHandler.removeMessages(what);
1314            }
1315        }
1316
1317        private synchronized boolean hasMessages(int what) {
1318            if (mBlockMessages) {
1319                return false;
1320            }
1321            if (mMessages != null) {
1322                Log.w(LOGTAG, "hasMessages() is not supported in this case.");
1323                return false;
1324            } else {
1325                return mHandler.hasMessages(what);
1326            }
1327        }
1328
1329        private synchronized void sendMessageDelayed(Message msg, long delay) {
1330            if (mBlockMessages) {
1331                return;
1332            }
1333            mHandler.sendMessageDelayed(msg, delay);
1334        }
1335
1336        /**
1337         * Send a message internally to the front of the queue.
1338         */
1339        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1340            if (mBlockMessages) {
1341                return;
1342            }
1343            if (mMessages != null) {
1344                mMessages.add(0, msg);
1345            } else {
1346                mHandler.sendMessageAtFrontOfQueue(msg);
1347            }
1348        }
1349
1350        /**
1351         * Remove all the messages.
1352         */
1353        private synchronized void removeMessages() {
1354            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1355            mDrawIsScheduled = false;
1356            mSplitPictureIsScheduled = false;
1357            if (mMessages != null) {
1358                mMessages.clear();
1359            } else {
1360                mHandler.removeCallbacksAndMessages(null);
1361            }
1362        }
1363
1364        /**
1365         * Block sending messages to the EventHub.
1366         */
1367        private synchronized void blockMessages() {
1368            mBlockMessages = true;
1369        }
1370    }
1371
1372    //-------------------------------------------------------------------------
1373    // Methods called by host activity (in the same thread)
1374    //-------------------------------------------------------------------------
1375
1376    void stopLoading() {
1377        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1378        if (mBrowserFrame != null) {
1379            mBrowserFrame.stopLoading();
1380        }
1381    }
1382
1383    //-------------------------------------------------------------------------
1384    // Methods called by WebView
1385    // If it refers to local variable, it needs synchronized().
1386    // If it needs WebCore, it has to send message.
1387    //-------------------------------------------------------------------------
1388
1389    void sendMessage(Message msg) {
1390        mEventHub.sendMessage(msg);
1391    }
1392
1393    void sendMessage(int what) {
1394        mEventHub.sendMessage(Message.obtain(null, what));
1395    }
1396
1397    void sendMessage(int what, Object obj) {
1398        mEventHub.sendMessage(Message.obtain(null, what, obj));
1399    }
1400
1401    void sendMessage(int what, int arg1) {
1402        // just ignore the second argument (make it 0)
1403        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1404    }
1405
1406    void sendMessage(int what, int arg1, int arg2) {
1407        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1408    }
1409
1410    void sendMessage(int what, int arg1, Object obj) {
1411        // just ignore the second argument (make it 0)
1412        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1413    }
1414
1415    void sendMessage(int what, int arg1, int arg2, Object obj) {
1416        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1417    }
1418
1419    void sendMessageDelayed(int what, Object obj, long delay) {
1420        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1421    }
1422
1423    void removeMessages(int what) {
1424        mEventHub.removeMessages(what);
1425    }
1426
1427    void removeMessages() {
1428        mEventHub.removeMessages();
1429    }
1430
1431    /**
1432     * Removes pending messages and trigger a DESTROY message to send to
1433     * WebCore.
1434     * Called from UI thread.
1435     */
1436    void destroy() {
1437        // We don't want anyone to post a message between removing pending
1438        // messages and sending the destroy message.
1439        synchronized (mEventHub) {
1440            // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
1441            // be preserved even the WebView is destroyed.
1442            // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
1443            boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
1444            boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
1445            mEventHub.removeMessages();
1446            mEventHub.sendMessageAtFrontOfQueue(
1447                    Message.obtain(null, EventHub.DESTROY));
1448            if (hasPause) {
1449                mEventHub.sendMessageAtFrontOfQueue(
1450                        Message.obtain(null, EventHub.PAUSE_TIMERS));
1451            }
1452            if (hasResume) {
1453                mEventHub.sendMessageAtFrontOfQueue(
1454                        Message.obtain(null, EventHub.RESUME_TIMERS));
1455            }
1456            mEventHub.blockMessages();
1457        }
1458    }
1459
1460    //-------------------------------------------------------------------------
1461    // WebViewCore private methods
1462    //-------------------------------------------------------------------------
1463
1464    private void clearCache(boolean includeDiskFiles) {
1465        mBrowserFrame.clearCache();
1466        if (includeDiskFiles) {
1467            CacheManager.removeAllCacheFiles();
1468        }
1469    }
1470
1471    private void loadUrl(String url) {
1472        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1473        mBrowserFrame.loadUrl(url);
1474    }
1475
1476    private void key(KeyEvent evt, boolean isDown) {
1477        if (DebugFlags.WEB_VIEW_CORE) {
1478            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1479                    + evt);
1480        }
1481        int keyCode = evt.getKeyCode();
1482        if (!nativeKey(keyCode, evt.getUnicodeChar(),
1483                evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
1484                evt.isSymPressed(),
1485                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1486            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1487                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1488                if (DebugFlags.WEB_VIEW_CORE) {
1489                    Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
1490                }
1491                if (mWebView != null && evt.isDown()) {
1492                    Message.obtain(mWebView.mPrivateHandler,
1493                            WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
1494                }
1495                return;
1496            }
1497            // bubble up the event handling
1498            // but do not bubble up the ENTER key, which would open the search
1499            // bar without any text.
1500            mCallbackProxy.onUnhandledKeyEvent(evt);
1501        }
1502    }
1503
1504    // These values are used to avoid requesting a layout based on old values
1505    private int mCurrentViewWidth = 0;
1506    private int mCurrentViewHeight = 0;
1507    private float mCurrentViewScale = 1.0f;
1508
1509    // notify webkit that our virtual view size changed size (after inv-zoom)
1510    private void viewSizeChanged(int w, int h, int textwrapWidth, float scale,
1511            boolean ignoreHeight) {
1512        if (DebugFlags.WEB_VIEW_CORE) {
1513            Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
1514                    + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
1515        }
1516        if (w == 0) {
1517            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1518            return;
1519        }
1520        int width = w;
1521        if (mSettings.getUseWideViewPort()) {
1522            if (mViewportWidth == -1) {
1523                if (mSettings.getLayoutAlgorithm() ==
1524                        WebSettings.LayoutAlgorithm.NORMAL) {
1525                    width = DEFAULT_VIEWPORT_WIDTH;
1526                } else {
1527                    /*
1528                     * if a page's minimum preferred width is wider than the
1529                     * given "w", use it instead to get better layout result. If
1530                     * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1531                     * wider. If we start a page with screen width, due to the
1532                     * delay between {@link #didFirstLayout} and
1533                     * {@link #viewSizeChanged},
1534                     * {@link #nativeGetContentMinPrefWidth} will return a more
1535                     * accurate value than initial 0 to result a better layout.
1536                     * In the worse case, the native width will be adjusted when
1537                     * next zoom or screen orientation change happens.
1538                     */
1539                    width = Math.max(w, Math.max(DEFAULT_VIEWPORT_WIDTH,
1540                            nativeGetContentMinPrefWidth()));
1541                }
1542            } else {
1543                width = Math.max(w, mViewportWidth);
1544            }
1545        }
1546        nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
1547                textwrapWidth, scale, w, h, ignoreHeight);
1548        // Remember the current width and height
1549        boolean needInvalidate = (mCurrentViewWidth == 0);
1550        mCurrentViewWidth = w;
1551        mCurrentViewHeight = h;
1552        mCurrentViewScale = scale;
1553        if (needInvalidate) {
1554            // ensure {@link #webkitDraw} is called as we were blocking in
1555            // {@link #contentDraw} when mCurrentViewWidth is 0
1556            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1557            contentDraw();
1558        }
1559        mEventHub.sendMessage(Message.obtain(null,
1560                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1561    }
1562
1563    private void sendUpdateTextEntry() {
1564        if (mWebView != null) {
1565            Message.obtain(mWebView.mPrivateHandler,
1566                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1567        }
1568    }
1569
1570    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1571    // callbacks. Computes the sum of database quota for all origins.
1572    private long getUsedQuota() {
1573        WebStorage webStorage = WebStorage.getInstance();
1574        Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
1575
1576        if (origins == null) {
1577            return 0;
1578        }
1579        long usedQuota = 0;
1580        for (WebStorage.Origin website : origins) {
1581            usedQuota += website.getQuota();
1582        }
1583        return usedQuota;
1584    }
1585
1586    // Used to avoid posting more than one draw message.
1587    private boolean mDrawIsScheduled;
1588
1589    // Used to avoid posting more than one split picture message.
1590    private boolean mSplitPictureIsScheduled;
1591
1592    // Used to suspend drawing.
1593    private boolean mDrawIsPaused;
1594
1595    // mRestoreState is set in didFirstLayout(), and reset in the next
1596    // webkitDraw after passing it to the UI thread.
1597    private RestoreState mRestoreState = null;
1598
1599    static class RestoreState {
1600        float mMinScale;
1601        float mMaxScale;
1602        float mViewScale;
1603        float mTextWrapScale;
1604        float mDefaultScale;
1605        int mScrollX;
1606        int mScrollY;
1607        boolean mMobileSite;
1608    }
1609
1610    static class DrawData {
1611        DrawData() {
1612            mInvalRegion = new Region();
1613            mWidthHeight = new Point();
1614        }
1615        Region mInvalRegion;
1616        Point mViewPoint;
1617        Point mWidthHeight;
1618        int mMinPrefWidth;
1619        RestoreState mRestoreState; // only non-null if it is for the first
1620                                    // picture set after the first layout
1621        boolean mFocusSizeChanged;
1622    }
1623
1624    private void webkitDraw() {
1625        mDrawIsScheduled = false;
1626        DrawData draw = new DrawData();
1627        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
1628        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
1629                == false) {
1630            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
1631            return;
1632        }
1633        if (mWebView != null) {
1634            // Send the native view size that was used during the most recent
1635            // layout.
1636            draw.mFocusSizeChanged = nativeFocusBoundsChanged();
1637            draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1638            if (mSettings.getUseWideViewPort()) {
1639                draw.mMinPrefWidth = Math.max(
1640                        mViewportWidth == -1 ? DEFAULT_VIEWPORT_WIDTH
1641                                : (mViewportWidth == 0 ? mCurrentViewWidth
1642                                        : mViewportWidth),
1643                        nativeGetContentMinPrefWidth());
1644            }
1645            if (mRestoreState != null) {
1646                draw.mRestoreState = mRestoreState;
1647                mRestoreState = null;
1648            }
1649            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1650            Message.obtain(mWebView.mPrivateHandler,
1651                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
1652            if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1653                // as we have the new picture, try to sync the scroll position
1654                Message.obtain(mWebView.mPrivateHandler,
1655                        WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1656                        mWebkitScrollY).sendToTarget();
1657                mWebkitScrollX = mWebkitScrollY = 0;
1658            }
1659        }
1660    }
1661
1662    ///////////////////////////////////////////////////////////////////////////
1663    // These are called from the UI thread, not our thread
1664
1665    static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
1666                                         Paint.DITHER_FLAG |
1667                                         Paint.SUBPIXEL_TEXT_FLAG;
1668    static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
1669                                           Paint.DITHER_FLAG;
1670
1671    final DrawFilter mZoomFilter =
1672                    new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
1673    final DrawFilter mScrollFilter = null;
1674    // If we need to trade more speed for less quality on slower devices
1675    // use this: new PaintFlagsDrawFilter(SCROLL_BITS, 0);
1676
1677    /* package */ void drawContentPicture(Canvas canvas, int color,
1678                                          boolean animatingZoom,
1679                                          boolean animatingScroll) {
1680        DrawFilter df = null;
1681        if (animatingZoom) {
1682            df = mZoomFilter;
1683        } else if (animatingScroll) {
1684            df = mScrollFilter;
1685        }
1686        canvas.setDrawFilter(df);
1687        boolean tookTooLong = nativeDrawContent(canvas, color);
1688        canvas.setDrawFilter(null);
1689        if (tookTooLong && mSplitPictureIsScheduled == false) {
1690            mSplitPictureIsScheduled = true;
1691            sendMessage(EventHub.SPLIT_PICTURE_SET);
1692        }
1693    }
1694
1695    /* package */ synchronized boolean pictureReady() {
1696        return 0 != mNativeClass ? nativePictureReady() : false;
1697    }
1698
1699    /*package*/ synchronized Picture copyContentPicture() {
1700        Picture result = new Picture();
1701        if (0 != mNativeClass) {
1702            nativeCopyContentToPicture(result);
1703        }
1704        return result;
1705    }
1706
1707    static void pauseUpdate(WebViewCore core) {
1708        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1709        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1710        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1711        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1712                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1713        // Note: there is one possible failure mode. If pauseUpdate() is called
1714        // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
1715        // of the queue and about to be executed. mDrawIsScheduled may be set to
1716        // false in webkitDraw(). So update won't be blocked. But at least the
1717        // webcore thread priority is still lowered.
1718        if (core != null) {
1719            synchronized (core) {
1720                core.mDrawIsPaused = true;
1721                core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1722            }
1723        }
1724    }
1725
1726    static void resumeUpdate(WebViewCore core) {
1727        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1728        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1729        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1730        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1731                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1732        if (core != null) {
1733            synchronized (core) {
1734                core.mDrawIsScheduled = false;
1735                core.mDrawIsPaused = false;
1736                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate");
1737                core.contentDraw();
1738            }
1739        }
1740    }
1741
1742    static void startCacheTransaction() {
1743        sWebCoreHandler.sendMessage(sWebCoreHandler
1744                .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
1745    }
1746
1747    static void endCacheTransaction() {
1748        sWebCoreHandler.sendMessage(sWebCoreHandler
1749                .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
1750    }
1751
1752    //////////////////////////////////////////////////////////////////////////
1753
1754    private void restoreState(int index) {
1755        WebBackForwardList list = mCallbackProxy.getBackForwardList();
1756        int size = list.getSize();
1757        for (int i = 0; i < size; i++) {
1758            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1759        }
1760        mBrowserFrame.mLoadInitFromJava = true;
1761        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1762        mBrowserFrame.mLoadInitFromJava = false;
1763    }
1764
1765    //-------------------------------------------------------------------------
1766    // Implement abstract methods in WebViewCore, native WebKit callback part
1767    //-------------------------------------------------------------------------
1768
1769    // called from JNI or WebView thread
1770    /* package */ void contentDraw() {
1771        // don't update the Picture until we have an initial width and finish
1772        // the first layout
1773        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1774            return;
1775        }
1776        // only fire an event if this is our first request
1777        synchronized (this) {
1778            if (mDrawIsPaused || mDrawIsScheduled) {
1779                return;
1780            }
1781            mDrawIsScheduled = true;
1782            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1783        }
1784    }
1785
1786    // called by JNI
1787    private void contentScrollBy(int dx, int dy, boolean animate) {
1788        if (!mBrowserFrame.firstLayoutDone()) {
1789            // Will this happen? If yes, we need to do something here.
1790            return;
1791        }
1792        if (mWebView != null) {
1793            Message msg = Message.obtain(mWebView.mPrivateHandler,
1794                    WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
1795            if (mDrawIsScheduled) {
1796                mEventHub.sendMessage(Message.obtain(null,
1797                        EventHub.MESSAGE_RELAY, msg));
1798            } else {
1799                msg.sendToTarget();
1800            }
1801        }
1802    }
1803
1804    // called by JNI
1805    private void contentScrollTo(int x, int y) {
1806        if (!mBrowserFrame.firstLayoutDone()) {
1807            /*
1808             * WebKit restore state will be called before didFirstLayout(),
1809             * remember the position as it has to be applied after restoring
1810             * zoom factor which is controlled by screenWidth.
1811             */
1812            mRestoredX = x;
1813            mRestoredY = y;
1814            return;
1815        }
1816        if (mWebView != null) {
1817            Message msg = Message.obtain(mWebView.mPrivateHandler,
1818                    WebView.SCROLL_TO_MSG_ID, x, y);
1819            if (mDrawIsScheduled) {
1820                mEventHub.sendMessage(Message.obtain(null,
1821                        EventHub.MESSAGE_RELAY, msg));
1822            } else {
1823                msg.sendToTarget();
1824            }
1825        }
1826    }
1827
1828    // called by JNI
1829    private void contentSpawnScrollTo(int x, int y) {
1830        if (!mBrowserFrame.firstLayoutDone()) {
1831            /*
1832             * WebKit restore state will be called before didFirstLayout(),
1833             * remember the position as it has to be applied after restoring
1834             * zoom factor which is controlled by screenWidth.
1835             */
1836            mRestoredX = x;
1837            mRestoredY = y;
1838            return;
1839        }
1840        if (mWebView != null) {
1841            Message msg = Message.obtain(mWebView.mPrivateHandler,
1842                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
1843            if (mDrawIsScheduled) {
1844                mEventHub.sendMessage(Message.obtain(null,
1845                        EventHub.MESSAGE_RELAY, msg));
1846            } else {
1847                msg.sendToTarget();
1848            }
1849        }
1850    }
1851
1852    // called by JNI
1853    private void sendNotifyProgressFinished() {
1854        sendUpdateTextEntry();
1855        // as CacheManager can behave based on database transaction, we need to
1856        // call tick() to trigger endTransaction
1857        sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
1858        sWebCoreHandler.sendMessage(sWebCoreHandler
1859                .obtainMessage(WebCoreThread.CACHE_TICKER));
1860        contentDraw();
1861    }
1862
1863    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
1864        be scaled before they can be used by the view system, which happens
1865        in WebView since it (and its thread) know the current scale factor.
1866     */
1867    private void sendViewInvalidate(int left, int top, int right, int bottom) {
1868        if (mWebView != null) {
1869            Message.obtain(mWebView.mPrivateHandler,
1870                           WebView.INVAL_RECT_MSG_ID,
1871                           new Rect(left, top, right, bottom)).sendToTarget();
1872        }
1873    }
1874
1875    /* package */ WebView getWebView() {
1876        return mWebView;
1877    }
1878
1879    private native void setViewportSettingsFromNative();
1880
1881    // called by JNI
1882    private void didFirstLayout(boolean standardLoad) {
1883        if (DebugFlags.WEB_VIEW_CORE) {
1884            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
1885        }
1886
1887        mBrowserFrame.didFirstLayout();
1888
1889        if (mWebView == null) return;
1890
1891        setupViewport(standardLoad || mRestoredScale > 0);
1892
1893        // reset the scroll position, the restored offset and scales
1894        mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY
1895                = mRestoredScale = mRestoredScreenWidthScale = 0;
1896    }
1897
1898    // called by JNI
1899    private void updateViewport() {
1900        // if updateViewport is called before first layout, wait until first
1901        // layout to update the viewport. In the rare case, this is called after
1902        // first layout, force an update as we have just parsed the viewport
1903        // meta tag.
1904        if (mBrowserFrame.firstLayoutDone()) {
1905            setupViewport(true);
1906        }
1907    }
1908
1909    private void setupViewport(boolean updateRestoreState) {
1910        // set the viewport settings from WebKit
1911        setViewportSettingsFromNative();
1912
1913        // adjust the default scale to match the densityDpi
1914        float adjust = 1.0f;
1915        if (mViewportDensityDpi == -1) {
1916            if (WebView.DEFAULT_SCALE_PERCENT != 100) {
1917                adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f;
1918            }
1919        } else if (mViewportDensityDpi > 0) {
1920            adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
1921                    / mViewportDensityDpi;
1922        }
1923        int defaultScale = (int) (adjust * 100);
1924
1925        if (mViewportInitialScale > 0) {
1926            mViewportInitialScale *= adjust;
1927        }
1928        if (mViewportMinimumScale > 0) {
1929            mViewportMinimumScale *= adjust;
1930        }
1931        if (mViewportMaximumScale > 0) {
1932            mViewportMaximumScale *= adjust;
1933        }
1934
1935        // infer the values if they are not defined.
1936        if (mViewportWidth == 0) {
1937            if (mViewportInitialScale == 0) {
1938                mViewportInitialScale = defaultScale;
1939            }
1940        }
1941        if (mViewportUserScalable == false) {
1942            mViewportInitialScale = defaultScale;
1943            mViewportMinimumScale = defaultScale;
1944            mViewportMaximumScale = defaultScale;
1945        }
1946        if (mViewportMinimumScale > mViewportInitialScale
1947                && mViewportInitialScale != 0) {
1948            mViewportMinimumScale = mViewportInitialScale;
1949        }
1950        if (mViewportMaximumScale > 0
1951                && mViewportMaximumScale < mViewportInitialScale) {
1952            mViewportMaximumScale = mViewportInitialScale;
1953        }
1954        if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
1955            mViewportWidth = 0;
1956        }
1957
1958        // if mViewportWidth is 0, it means device-width, always update.
1959        if (mViewportWidth != 0 && !updateRestoreState) return;
1960
1961        // now notify webview
1962        // webViewWidth refers to the width in the view system
1963        int webViewWidth;
1964        // viewportWidth refers to the width in the document system
1965        int viewportWidth = mCurrentViewWidth;
1966        if (viewportWidth == 0) {
1967            // this may happen when WebView just starts. This is not perfect as
1968            // we call WebView method from WebCore thread. But not perfect
1969            // reference is better than no reference.
1970            webViewWidth = mWebView.getViewWidth();
1971            viewportWidth = (int) (webViewWidth / adjust);
1972            if (viewportWidth == 0) {
1973                Log.w(LOGTAG, "Can't get the viewWidth after the first layout");
1974            }
1975        } else {
1976            webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
1977        }
1978        mRestoreState = new RestoreState();
1979        mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
1980        mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
1981        mRestoreState.mDefaultScale = adjust;
1982        mRestoreState.mScrollX = mRestoredX;
1983        mRestoreState.mScrollY = mRestoredY;
1984        mRestoreState.mMobileSite = (0 == mViewportWidth);
1985        if (mRestoredScale > 0) {
1986            if (mRestoredScreenWidthScale > 0) {
1987                mRestoreState.mTextWrapScale =
1988                        mRestoredScreenWidthScale / 100.0f;
1989                // 0 will trigger WebView to turn on zoom overview mode
1990                mRestoreState.mViewScale = 0;
1991            } else {
1992                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1993                        mRestoredScale / 100.0f;
1994            }
1995        } else {
1996            if (mViewportInitialScale > 0) {
1997                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1998                        mViewportInitialScale / 100.0f;
1999            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
2000                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
2001                        (float) webViewWidth / mViewportWidth;
2002            } else {
2003                mRestoreState.mTextWrapScale = adjust;
2004                // 0 will trigger WebView to turn on zoom overview mode
2005                mRestoreState.mViewScale = 0;
2006            }
2007        }
2008
2009        if (mWebView.mHeightCanMeasure) {
2010            // Trick to ensure that the Picture has the exact height for the
2011            // content by forcing to layout with 0 height after the page is
2012            // ready, which is indicated by didFirstLayout. This is essential to
2013            // get rid of the white space in the GMail which uses WebView for
2014            // message view.
2015            mWebView.mLastHeightSent = 0;
2016            // Send a negative scale to indicate that WebCore should reuse
2017            // the current scale
2018            WebView.ViewSizeData data = new WebView.ViewSizeData();
2019            data.mWidth = mWebView.mLastWidthSent;
2020            data.mHeight = 0;
2021            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
2022            // true. It is safe to use mWidth for mTextWrapWidth.
2023            data.mTextWrapWidth = data.mWidth;
2024            data.mScale = -1.0f;
2025            data.mIgnoreHeight = false;
2026            // send VIEW_SIZE_CHANGED to the front of the queue so that we can
2027            // avoid pushing the wrong picture to the WebView side. If there is
2028            // a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
2029            // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED
2030            // in the queue, as mLastHeightSent has been updated here, we may
2031            // miss the requestLayout in WebView side after the new picture.
2032            mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2033            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2034                    EventHub.VIEW_SIZE_CHANGED, data));
2035        } else if (mSettings.getUseWideViewPort()) {
2036            if (viewportWidth == 0) {
2037                // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
2038                // to WebViewCore
2039                mWebView.mLastWidthSent = 0;
2040            } else {
2041                WebView.ViewSizeData data = new WebView.ViewSizeData();
2042                // mViewScale as 0 means it is in zoom overview mode. So we don't
2043                // know the exact scale. If mRestoredScale is non-zero, use it;
2044                // otherwise just use mTextWrapScale as the initial scale.
2045                data.mScale = mRestoreState.mViewScale == 0
2046                        ? (mRestoredScale > 0 ? mRestoredScale / 100.0f
2047                                : mRestoreState.mTextWrapScale)
2048                        : mRestoreState.mViewScale;
2049                if (DebugFlags.WEB_VIEW_CORE) {
2050                    Log.v(LOGTAG, "setupViewport"
2051                             + " mRestoredScale=" + mRestoredScale
2052                             + " mViewScale=" + mRestoreState.mViewScale
2053                             + " mTextWrapScale=" + mRestoreState.mTextWrapScale
2054                             );
2055                }
2056                data.mWidth = Math.round(webViewWidth / data.mScale);
2057                data.mHeight = mCurrentViewHeight * data.mWidth / viewportWidth;
2058                data.mTextWrapWidth = Math.round(webViewWidth
2059                        / mRestoreState.mTextWrapScale);
2060                data.mIgnoreHeight = false;
2061                // send VIEW_SIZE_CHANGED to the front of the queue so that we
2062                // can avoid pushing the wrong picture to the WebView side.
2063                mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2064                mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2065                        EventHub.VIEW_SIZE_CHANGED, data));
2066            }
2067        }
2068    }
2069
2070    // called by JNI
2071    private void restoreScale(int scale) {
2072        if (mBrowserFrame.firstLayoutDone() == false) {
2073            mRestoredScale = scale;
2074        }
2075    }
2076
2077    // called by JNI
2078    private void restoreScreenWidthScale(int scale) {
2079        if (!mSettings.getUseWideViewPort()) {
2080            return;
2081        }
2082
2083        if (mBrowserFrame.firstLayoutDone() == false) {
2084            mRestoredScreenWidthScale = scale;
2085        }
2086    }
2087
2088    // called by JNI
2089    private void needTouchEvents(boolean need) {
2090        if (mWebView != null) {
2091            Message.obtain(mWebView.mPrivateHandler,
2092                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
2093                    .sendToTarget();
2094        }
2095    }
2096
2097    // called by JNI
2098    private void updateTextfield(int ptr, boolean changeToPassword,
2099            String text, int textGeneration) {
2100        if (mWebView != null) {
2101            Message msg = Message.obtain(mWebView.mPrivateHandler,
2102                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
2103                    textGeneration, text);
2104            msg.getData().putBoolean("password", changeToPassword);
2105            msg.sendToTarget();
2106        }
2107    }
2108
2109    // called by JNI
2110    private void updateTextSelection(int pointer, int start, int end,
2111            int textGeneration) {
2112        if (mWebView != null) {
2113            Message.obtain(mWebView.mPrivateHandler,
2114                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
2115                new TextSelectionData(start, end)).sendToTarget();
2116        }
2117    }
2118
2119    // called by JNI
2120    private void clearTextEntry() {
2121        if (mWebView == null) return;
2122        Message.obtain(mWebView.mPrivateHandler,
2123                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
2124    }
2125
2126    // called by JNI
2127    private void sendFindAgain() {
2128        if (mWebView == null) return;
2129        Message.obtain(mWebView.mPrivateHandler,
2130                WebView.FIND_AGAIN).sendToTarget();
2131    }
2132
2133    private native void nativeUpdateFrameCacheIfLoading();
2134
2135    /**
2136     * Scroll the focused textfield to (xPercent, y) in document space
2137     */
2138    private native void nativeScrollFocusedTextInput(float xPercent, int y);
2139
2140    // these must be in document space (i.e. not scaled/zoomed).
2141    private native void nativeSetScrollOffset(int gen, int dx, int dy);
2142
2143    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
2144
2145    // called by JNI
2146    private void requestListBox(String[] array, int[] enabledArray,
2147            int[] selectedArray) {
2148        if (mWebView != null) {
2149            mWebView.requestListBox(array, enabledArray, selectedArray);
2150        }
2151    }
2152
2153    // called by JNI
2154    private void requestListBox(String[] array, int[] enabledArray,
2155            int selection) {
2156        if (mWebView != null) {
2157            mWebView.requestListBox(array, enabledArray, selection);
2158        }
2159
2160    }
2161
2162    // called by JNI
2163    private void requestKeyboard(boolean showKeyboard) {
2164        if (mWebView != null) {
2165            Message.obtain(mWebView.mPrivateHandler,
2166                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
2167                    .sendToTarget();
2168        }
2169    }
2170
2171    // called by JNI
2172    private Class<?> getPluginClass(String libName, String clsName) {
2173
2174        if (mWebView == null) {
2175            return null;
2176        }
2177
2178        String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName);
2179        if (pkgName == null) {
2180            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2181            return null;
2182        }
2183
2184        Class<?> pluginClass = null;
2185        try {
2186            pluginClass = PluginUtil.getPluginClass(mWebView.getContext(), pkgName, clsName);
2187        } catch (NameNotFoundException e) {
2188            Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")");
2189        } catch (ClassNotFoundException e) {
2190            Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
2191                    ") in the apk (" + pkgName + ")");
2192        }
2193
2194        return pluginClass;
2195    }
2196
2197    // called by JNI. PluginWidget function to launch an activity and overlays
2198    // the activity with the View provided by the plugin class.
2199    private void startFullScreenPluginActivity(String libName, String clsName, int npp) {
2200        if (mWebView == null) {
2201            return;
2202        }
2203
2204        String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName);
2205        if (pkgName == null) {
2206            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2207            return;
2208        }
2209
2210        Intent intent = new Intent("android.intent.webkit.PLUGIN");
2211        intent.putExtra(PluginActivity.INTENT_EXTRA_PACKAGE_NAME, pkgName);
2212        intent.putExtra(PluginActivity.INTENT_EXTRA_CLASS_NAME, clsName);
2213        intent.putExtra(PluginActivity.INTENT_EXTRA_NPP_INSTANCE, npp);
2214        mWebView.getContext().startActivity(intent);
2215    }
2216
2217    // called by JNI.  PluginWidget functions for creating an embedded View for
2218    // the surface drawing model.
2219    private ViewManager.ChildView createSurface(String libName, String clsName,
2220            int npp, int x, int y, int width, int height) {
2221        if (mWebView == null) {
2222            return null;
2223        }
2224
2225        String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName);
2226        if (pkgName == null) {
2227            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2228            return null;
2229        }
2230
2231        PluginStub stub =PluginUtil.getPluginStub(mWebView.getContext(),pkgName, clsName);
2232        if (stub == null) {
2233            Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
2234                    ") in the apk (" + pkgName + ")");
2235            return null;
2236        }
2237
2238        View pluginView = stub.getEmbeddedView(npp, mWebView.getContext());
2239
2240        ViewManager.ChildView view = mWebView.mViewManager.createView();
2241        view.mView = pluginView;
2242        view.attachView(x, y, width, height);
2243        return view;
2244    }
2245
2246    private void updateSurface(ViewManager.ChildView childView, int x, int y,
2247            int width, int height) {
2248        childView.attachView(x, y, width, height);
2249    }
2250
2251    private void destroySurface(ViewManager.ChildView childView) {
2252        childView.removeView();
2253    }
2254
2255    private native void nativePause();
2256    private native void nativeResume();
2257    private native void nativeFreeMemory();
2258}
2259