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