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