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