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