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