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