WebViewCore.java revision 9ab32b66428cea7d0f8edb49d7e358d014c3d39b
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    }
712
713    static class GeolocationPermissionsData {
714        String mOrigin;
715        boolean mAllow;
716        boolean mRemember;
717    }
718
719
720
721        static final String[] HandlerDebugString = {
722            "REQUEST_LABEL", // 97
723            "UPDATE_FRAME_CACHE_IF_LOADING", // = 98
724            "SCROLL_TEXT_INPUT", // = 99
725            "LOAD_URL", // = 100;
726            "STOP_LOADING", // = 101;
727            "RELOAD", // = 102;
728            "KEY_DOWN", // = 103;
729            "KEY_UP", // = 104;
730            "VIEW_SIZE_CHANGED", // = 105;
731            "GO_BACK_FORWARD", // = 106;
732            "SET_SCROLL_OFFSET", // = 107;
733            "RESTORE_STATE", // = 108;
734            "PAUSE_TIMERS", // = 109;
735            "RESUME_TIMERS", // = 110;
736            "CLEAR_CACHE", // = 111;
737            "CLEAR_HISTORY", // = 112;
738            "SET_SELECTION", // = 113;
739            "REPLACE_TEXT", // = 114;
740            "PASS_TO_JS", // = 115;
741            "SET_GLOBAL_BOUNDS", // = 116;
742            "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117;
743            "CLICK", // = 118;
744            "SET_NETWORK_STATE", // = 119;
745            "DOC_HAS_IMAGES", // = 120;
746            "121", // = 121;
747            "DELETE_SELECTION", // = 122;
748            "LISTBOX_CHOICES", // = 123;
749            "SINGLE_LISTBOX_CHOICE", // = 124;
750            "MESSAGE_RELAY", // = 125;
751            "SET_BACKGROUND_COLOR", // = 126;
752            "SET_MOVE_FOCUS", // = 127
753            "SAVE_DOCUMENT_STATE", // = 128;
754            "129", // = 129;
755            "WEBKIT_DRAW", // = 130;
756            "SYNC_SCROLL", // = 131;
757            "POST_URL", // = 132;
758            "SPLIT_PICTURE_SET", // = 133;
759            "CLEAR_CONTENT", // = 134;
760            "SET_MOVE_MOUSE", // = 135;
761            "SET_MOVE_MOUSE_IF_LATEST", // = 136;
762            "REQUEST_CURSOR_HREF", // = 137;
763            "ADD_JS_INTERFACE", // = 138;
764            "LOAD_DATA", // = 139;
765            "TOUCH_UP", // = 140;
766            "TOUCH_EVENT", // = 141;
767            "SET_ACTIVE", // = 142;
768            "ON_PAUSE",     // = 143
769            "ON_RESUME",    // = 144
770            "FREE_MEMORY",  // = 145
771            "VALID_NODE_BOUNDS", // = 146
772        };
773
774    class EventHub {
775        // Message Ids
776        static final int REQUEST_LABEL = 97;
777        static final int UPDATE_FRAME_CACHE_IF_LOADING = 98;
778        static final int SCROLL_TEXT_INPUT = 99;
779        static final int LOAD_URL = 100;
780        static final int STOP_LOADING = 101;
781        static final int RELOAD = 102;
782        static final int KEY_DOWN = 103;
783        static final int KEY_UP = 104;
784        static final int VIEW_SIZE_CHANGED = 105;
785        static final int GO_BACK_FORWARD = 106;
786        static final int SET_SCROLL_OFFSET = 107;
787        static final int RESTORE_STATE = 108;
788        static final int PAUSE_TIMERS = 109;
789        static final int RESUME_TIMERS = 110;
790        static final int CLEAR_CACHE = 111;
791        static final int CLEAR_HISTORY = 112;
792        static final int SET_SELECTION = 113;
793        static final int REPLACE_TEXT = 114;
794        static final int PASS_TO_JS = 115;
795        static final int SET_GLOBAL_BOUNDS = 116;
796        static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117;
797        static final int CLICK = 118;
798        static final int SET_NETWORK_STATE = 119;
799        static final int DOC_HAS_IMAGES = 120;
800        static final int DELETE_SELECTION = 122;
801        static final int LISTBOX_CHOICES = 123;
802        static final int SINGLE_LISTBOX_CHOICE = 124;
803        static final int MESSAGE_RELAY = 125;
804        static final int SET_BACKGROUND_COLOR = 126;
805        static final int SET_MOVE_FOCUS = 127;
806        static final int SAVE_DOCUMENT_STATE = 128;
807
808        static final int WEBKIT_DRAW = 130;
809        static final int SYNC_SCROLL = 131;
810        static final int POST_URL = 132;
811        static final int SPLIT_PICTURE_SET = 133;
812        static final int CLEAR_CONTENT = 134;
813
814        // UI nav messages
815        static final int SET_MOVE_MOUSE = 135;
816        static final int SET_MOVE_MOUSE_IF_LATEST = 136;
817        static final int REQUEST_CURSOR_HREF = 137;
818        static final int ADD_JS_INTERFACE = 138;
819        static final int LOAD_DATA = 139;
820
821        // motion
822        static final int TOUCH_UP = 140;
823        // message used to pass UI touch events to WebCore
824        static final int TOUCH_EVENT = 141;
825
826        // Used to tell the focus controller not to draw the blinking cursor,
827        // based on whether the WebView has focus and whether the WebView's
828        // cursor matches the webpage's focus.
829        static final int SET_ACTIVE = 142;
830
831        // lifecycle activities for just this DOM (unlike pauseTimers, which
832        // is global)
833        static final int ON_PAUSE = 143;
834        static final int ON_RESUME = 144;
835        static final int FREE_MEMORY = 145;
836        static final int VALID_NODE_BOUNDS = 146;
837
838        // Network-based messaging
839        static final int CLEAR_SSL_PREF_TABLE = 150;
840
841        // Test harness messages
842        static final int REQUEST_EXT_REPRESENTATION = 160;
843        static final int REQUEST_DOC_AS_TEXT = 161;
844
845        // debugging
846        static final int DUMP_DOMTREE = 170;
847        static final int DUMP_RENDERTREE = 171;
848        static final int DUMP_NAVTREE = 172;
849        static final int DUMP_V8COUNTERS = 173;
850
851        static final int SET_JS_FLAGS = 174;
852        // Geolocation
853        static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180;
854
855        static final int POPULATE_VISITED_LINKS = 181;
856
857        static final int HIDE_FULLSCREEN = 182;
858
859        static final int SET_NETWORK_TYPE = 183;
860
861        // navigator.isApplicationInstalled()
862        static final int ADD_PACKAGE_NAMES = 184;
863        static final int ADD_PACKAGE_NAME = 185;
864        static final int REMOVE_PACKAGE_NAME = 186;
865
866        // private message ids
867        private static final int DESTROY =     200;
868
869        // Private handler for WebCore messages.
870        private Handler mHandler;
871        // Message queue for containing messages before the WebCore thread is
872        // ready.
873        private ArrayList<Message> mMessages = new ArrayList<Message>();
874        // Flag for blocking messages. This is used during DESTROY to avoid
875        // posting more messages to the EventHub or to WebView's event handler.
876        private boolean mBlockMessages;
877
878        private int mTid;
879        private int mSavedPriority;
880
881        /**
882         * Prevent other classes from creating an EventHub.
883         */
884        private EventHub() {}
885
886        /**
887         * Transfer all messages to the newly created webcore thread handler.
888         */
889        private void transferMessages() {
890            mTid = Process.myTid();
891            mSavedPriority = Process.getThreadPriority(mTid);
892
893            mHandler = new Handler() {
894                @Override
895                public void handleMessage(Message msg) {
896                    if (DebugFlags.WEB_VIEW_CORE) {
897                        Log.v(LOGTAG, (msg.what < REQUEST_LABEL
898                                || msg.what
899                                > VALID_NODE_BOUNDS ? Integer.toString(msg.what)
900                                : HandlerDebugString[msg.what
901                                        - REQUEST_LABEL])
902                                + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
903                                + " obj=" + msg.obj);
904                    }
905                    switch (msg.what) {
906                        case WEBKIT_DRAW:
907                            webkitDraw();
908                            break;
909
910                        case DESTROY:
911                            // Time to take down the world. Cancel all pending
912                            // loads and destroy the native view and frame.
913                            synchronized (WebViewCore.this) {
914                                mBrowserFrame.destroy();
915                                mBrowserFrame = null;
916                                mSettings.onDestroyed();
917                                mNativeClass = 0;
918                                mWebView = null;
919                            }
920                            break;
921
922                        case REQUEST_LABEL:
923                            if (mWebView != null) {
924                                int nodePointer = msg.arg2;
925                                String label = nativeRequestLabel(msg.arg1,
926                                        nodePointer);
927                                if (label != null && label.length() > 0) {
928                                    Message.obtain(mWebView.mPrivateHandler,
929                                            WebView.RETURN_LABEL, nodePointer,
930                                            0, label).sendToTarget();
931                                }
932                            }
933                            break;
934
935                        case UPDATE_FRAME_CACHE_IF_LOADING:
936                            nativeUpdateFrameCacheIfLoading();
937                            break;
938
939                        case SCROLL_TEXT_INPUT:
940                            nativeScrollFocusedTextInput(
941                                    ((Float) msg.obj).floatValue(), msg.arg1);
942                            break;
943
944                        case LOAD_URL: {
945                            GetUrlData param = (GetUrlData) msg.obj;
946                            loadUrl(param.mUrl, param.mExtraHeaders);
947                            break;
948                        }
949
950                        case POST_URL: {
951                            PostUrlData param = (PostUrlData) msg.obj;
952                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
953                            break;
954                        }
955                        case LOAD_DATA:
956                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
957                            String baseUrl = loadParams.mBaseUrl;
958                            if (baseUrl != null) {
959                                int i = baseUrl.indexOf(':');
960                                if (i > 0) {
961                                    /*
962                                     * In 1.0, {@link
963                                     * WebView#loadDataWithBaseURL} can access
964                                     * local asset files as long as the data is
965                                     * valid. In the new WebKit, the restriction
966                                     * is tightened. To be compatible with 1.0,
967                                     * we automatically add the scheme of the
968                                     * baseUrl for local access as long as it is
969                                     * not http(s)/ftp(s)/about/javascript
970                                     */
971                                    String scheme = baseUrl.substring(0, i);
972                                    if (!scheme.startsWith("http") &&
973                                            !scheme.startsWith("ftp") &&
974                                            !scheme.startsWith("about") &&
975                                            !scheme.startsWith("javascript")) {
976                                        nativeRegisterURLSchemeAsLocal(scheme);
977                                    }
978                                }
979                            }
980                            mBrowserFrame.loadData(baseUrl,
981                                    loadParams.mData,
982                                    loadParams.mMimeType,
983                                    loadParams.mEncoding,
984                                    loadParams.mHistoryUrl);
985                            break;
986
987                        case STOP_LOADING:
988                            // If the WebCore has committed the load, but not
989                            // finished the first layout yet, we need to set
990                            // first layout done to trigger the interpreted side sync
991                            // up with native side
992                            if (mBrowserFrame.committed()
993                                    && !mBrowserFrame.firstLayoutDone()) {
994                                mBrowserFrame.didFirstLayout();
995                            }
996                            // Do this after syncing up the layout state.
997                            stopLoading();
998                            break;
999
1000                        case RELOAD:
1001                            mBrowserFrame.reload(false);
1002                            break;
1003
1004                        case KEY_DOWN:
1005                            key((KeyEvent) msg.obj, true);
1006                            break;
1007
1008                        case KEY_UP:
1009                            key((KeyEvent) msg.obj, false);
1010                            break;
1011
1012                        case CLICK:
1013                            nativeClick(msg.arg1, msg.arg2);
1014                            break;
1015
1016                        case VIEW_SIZE_CHANGED: {
1017                            WebView.ViewSizeData data =
1018                                    (WebView.ViewSizeData) msg.obj;
1019                            viewSizeChanged(data.mWidth, data.mHeight,
1020                                    data.mTextWrapWidth, data.mScale,
1021                                    data.mAnchorX, data.mAnchorY,
1022                                    data.mIgnoreHeight);
1023                            break;
1024                        }
1025                        case SET_SCROLL_OFFSET:
1026                            // note: these are in document coordinates
1027                            // (inv-zoom)
1028                            Point pt = (Point) msg.obj;
1029                            nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
1030                            break;
1031
1032                        case SET_GLOBAL_BOUNDS:
1033                            Rect r = (Rect) msg.obj;
1034                            nativeSetGlobalBounds(r.left, r.top, r.width(),
1035                                r.height());
1036                            break;
1037
1038                        case GO_BACK_FORWARD:
1039                            // If it is a standard load and the load is not
1040                            // committed yet, we interpret BACK as RELOAD
1041                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
1042                                    (mBrowserFrame.loadType() ==
1043                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
1044                                mBrowserFrame.reload(true);
1045                            } else {
1046                                mBrowserFrame.goBackOrForward(msg.arg1);
1047                            }
1048                            break;
1049
1050                        case RESTORE_STATE:
1051                            stopLoading();
1052                            restoreState(msg.arg1);
1053                            break;
1054
1055                        case PAUSE_TIMERS:
1056                            mSavedPriority = Process.getThreadPriority(mTid);
1057                            Process.setThreadPriority(mTid,
1058                                    Process.THREAD_PRIORITY_BACKGROUND);
1059                            pauseTimers();
1060                            WebViewWorker.getHandler().sendEmptyMessage(
1061                                    WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION);
1062                            break;
1063
1064                        case RESUME_TIMERS:
1065                            Process.setThreadPriority(mTid, mSavedPriority);
1066                            resumeTimers();
1067                            WebViewWorker.getHandler().sendEmptyMessage(
1068                                    WebViewWorker.MSG_RESUME_CACHE_TRANSACTION);
1069                            break;
1070
1071                        case ON_PAUSE:
1072                            nativePause();
1073                            break;
1074
1075                        case ON_RESUME:
1076                            nativeResume();
1077                            break;
1078
1079                        case FREE_MEMORY:
1080                            clearCache(false);
1081                            nativeFreeMemory();
1082                            break;
1083
1084                        case SET_NETWORK_STATE:
1085                            if (BrowserFrame.sJavaBridge == null) {
1086                                throw new IllegalStateException("No WebView " +
1087                                        "has been created in this process!");
1088                            }
1089                            BrowserFrame.sJavaBridge
1090                                    .setNetworkOnLine(msg.arg1 == 1);
1091                            break;
1092
1093                        case SET_NETWORK_TYPE:
1094                            if (BrowserFrame.sJavaBridge == null) {
1095                                throw new IllegalStateException("No WebView " +
1096                                        "has been created in this process!");
1097                            }
1098                            Map<String, String> map = (Map<String, String>) msg.obj;
1099                            BrowserFrame.sJavaBridge
1100                                    .setNetworkType(map.get("type"), map.get("subtype"));
1101                            break;
1102
1103                        case CLEAR_CACHE:
1104                            clearCache(msg.arg1 == 1);
1105                            break;
1106
1107                        case CLEAR_HISTORY:
1108                            mCallbackProxy.getBackForwardList().
1109                                    close(mBrowserFrame.mNativeFrame);
1110                            break;
1111
1112                        case REPLACE_TEXT:
1113                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
1114                            nativeReplaceTextfieldText(msg.arg1, msg.arg2,
1115                                    rep.mReplace, rep.mNewStart, rep.mNewEnd,
1116                                    rep.mTextGeneration);
1117                            break;
1118
1119                        case PASS_TO_JS: {
1120                            JSKeyData jsData = (JSKeyData) msg.obj;
1121                            KeyEvent evt = jsData.mEvent;
1122                            int keyCode = evt.getKeyCode();
1123                            int keyValue = evt.getUnicodeChar();
1124                            int generation = msg.arg1;
1125                            passToJs(generation,
1126                                    jsData.mCurrentText,
1127                                    keyCode,
1128                                    keyValue,
1129                                    evt.isDown(),
1130                                    evt.isShiftPressed(), evt.isAltPressed(),
1131                                    evt.isSymPressed());
1132                            break;
1133                        }
1134
1135                        case SAVE_DOCUMENT_STATE: {
1136                            CursorData cDat = (CursorData) msg.obj;
1137                            nativeSaveDocumentState(cDat.mFrame);
1138                            break;
1139                        }
1140
1141                        case CLEAR_SSL_PREF_TABLE:
1142                            Network.getInstance(mContext)
1143                                    .clearUserSslPrefTable();
1144                            break;
1145
1146                        case TOUCH_UP:
1147                            TouchUpData touchUpData = (TouchUpData) msg.obj;
1148                            nativeTouchUp(touchUpData.mMoveGeneration,
1149                                    touchUpData.mFrame, touchUpData.mNode,
1150                                    touchUpData.mX, touchUpData.mY);
1151                            break;
1152
1153                        case TOUCH_EVENT: {
1154                            TouchEventData ted = (TouchEventData) msg.obj;
1155                            Message.obtain(
1156                                    mWebView.mPrivateHandler,
1157                                    WebView.PREVENT_TOUCH_ID,
1158                                    ted.mAction,
1159                                    nativeHandleTouchEvent(ted.mAction, ted.mX,
1160                                            ted.mY, ted.mMetaState) ? 1 : 0,
1161                                    ted.mReprocess ? ted : null).sendToTarget();
1162                            break;
1163                        }
1164
1165                        case SET_ACTIVE:
1166                            nativeSetFocusControllerActive(msg.arg1 == 1);
1167                            break;
1168
1169                        case ADD_JS_INTERFACE:
1170                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
1171                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
1172                                    jsData.mInterfaceName);
1173                            break;
1174
1175                        case REQUEST_EXT_REPRESENTATION:
1176                            mBrowserFrame.externalRepresentation(
1177                                    (Message) msg.obj);
1178                            break;
1179
1180                        case REQUEST_DOC_AS_TEXT:
1181                            mBrowserFrame.documentAsText((Message) msg.obj);
1182                            break;
1183
1184                        case SET_MOVE_FOCUS:
1185                            CursorData focusData = (CursorData) msg.obj;
1186                            nativeMoveFocus(focusData.mFrame, focusData.mNode);
1187                            break;
1188
1189                        case SET_MOVE_MOUSE:
1190                            CursorData cursorData = (CursorData) msg.obj;
1191                            nativeMoveMouse(cursorData.mFrame,
1192                                     cursorData.mX, cursorData.mY);
1193                            break;
1194
1195                        case SET_MOVE_MOUSE_IF_LATEST:
1196                            CursorData cData = (CursorData) msg.obj;
1197                            nativeMoveMouseIfLatest(cData.mMoveGeneration,
1198                                    cData.mFrame,
1199                                    cData.mX, cData.mY);
1200                            break;
1201
1202                        case REQUEST_CURSOR_HREF: {
1203                            Message hrefMsg = (Message) msg.obj;
1204                            hrefMsg.getData().putString("url",
1205                                    nativeRetrieveHref(msg.arg1, msg.arg2));
1206                            hrefMsg.getData().putString("title",
1207                                    nativeRetrieveAnchorText(msg.arg1, msg.arg2));
1208                            hrefMsg.sendToTarget();
1209                            break;
1210                        }
1211
1212                        case UPDATE_CACHE_AND_TEXT_ENTRY:
1213                            nativeUpdateFrameCache();
1214                            // FIXME: this should provide a minimal rectangle
1215                            if (mWebView != null) {
1216                                mWebView.postInvalidate();
1217                            }
1218                            sendUpdateTextEntry();
1219                            break;
1220
1221                        case DOC_HAS_IMAGES:
1222                            Message imageResult = (Message) msg.obj;
1223                            imageResult.arg1 =
1224                                    mBrowserFrame.documentHasImages() ? 1 : 0;
1225                            imageResult.sendToTarget();
1226                            break;
1227
1228                        case DELETE_SELECTION:
1229                            TextSelectionData deleteSelectionData
1230                                    = (TextSelectionData) msg.obj;
1231                            nativeDeleteSelection(deleteSelectionData.mStart,
1232                                    deleteSelectionData.mEnd, msg.arg1);
1233                            break;
1234
1235                        case SET_SELECTION:
1236                            nativeSetSelection(msg.arg1, msg.arg2);
1237                            break;
1238
1239                        case LISTBOX_CHOICES:
1240                            SparseBooleanArray choices = (SparseBooleanArray)
1241                                    msg.obj;
1242                            int choicesSize = msg.arg1;
1243                            boolean[] choicesArray = new boolean[choicesSize];
1244                            for (int c = 0; c < choicesSize; c++) {
1245                                choicesArray[c] = choices.get(c);
1246                            }
1247                            nativeSendListBoxChoices(choicesArray,
1248                                    choicesSize);
1249                            break;
1250
1251                        case SINGLE_LISTBOX_CHOICE:
1252                            nativeSendListBoxChoice(msg.arg1);
1253                            break;
1254
1255                        case SET_BACKGROUND_COLOR:
1256                            nativeSetBackgroundColor(msg.arg1);
1257                            break;
1258
1259                        case DUMP_DOMTREE:
1260                            nativeDumpDomTree(msg.arg1 == 1);
1261                            break;
1262
1263                        case DUMP_RENDERTREE:
1264                            nativeDumpRenderTree(msg.arg1 == 1);
1265                            break;
1266
1267                        case DUMP_NAVTREE:
1268                            nativeDumpNavTree();
1269                            break;
1270
1271                        case DUMP_V8COUNTERS:
1272                            nativeDumpV8Counters();
1273                            break;
1274
1275                        case SET_JS_FLAGS:
1276                            nativeSetJsFlags((String)msg.obj);
1277                            break;
1278
1279                        case GEOLOCATION_PERMISSIONS_PROVIDE:
1280                            GeolocationPermissionsData data =
1281                                    (GeolocationPermissionsData) msg.obj;
1282                            nativeGeolocationPermissionsProvide(data.mOrigin,
1283                                    data.mAllow, data.mRemember);
1284                            break;
1285
1286                        case SYNC_SCROLL:
1287                            mWebkitScrollX = msg.arg1;
1288                            mWebkitScrollY = msg.arg2;
1289                            break;
1290
1291                        case SPLIT_PICTURE_SET:
1292                            nativeSplitContent();
1293                            mSplitPictureIsScheduled = false;
1294                            break;
1295
1296                        case CLEAR_CONTENT:
1297                            // Clear the view so that onDraw() will draw nothing
1298                            // but white background
1299                            // (See public method WebView.clearView)
1300                            nativeClearContent();
1301                            break;
1302
1303                        case MESSAGE_RELAY:
1304                            if (msg.obj instanceof Message) {
1305                                ((Message) msg.obj).sendToTarget();
1306                            }
1307                            break;
1308
1309                        case POPULATE_VISITED_LINKS:
1310                            nativeProvideVisitedHistory((String[])msg.obj);
1311                            break;
1312
1313                        case VALID_NODE_BOUNDS: {
1314                            MotionUpData motionUpData = (MotionUpData) msg.obj;
1315                            if (!nativeValidNodeAndBounds(
1316                                    motionUpData.mFrame, motionUpData.mNode,
1317                                    motionUpData.mBounds)) {
1318                                nativeUpdateFrameCache();
1319                            }
1320                            Message message = mWebView.mPrivateHandler
1321                                    .obtainMessage(WebView.DO_MOTION_UP,
1322                                    motionUpData.mX, motionUpData.mY);
1323                            mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(
1324                                    message);
1325                            break;
1326                        }
1327
1328                        case HIDE_FULLSCREEN:
1329                            nativeFullScreenPluginHidden(msg.arg1);
1330                            break;
1331
1332                        case ADD_PACKAGE_NAMES:
1333                            if (BrowserFrame.sJavaBridge == null) {
1334                                throw new IllegalStateException("No WebView " +
1335                                        "has been created in this process!");
1336                            }
1337                            BrowserFrame.sJavaBridge.addPackageNames(
1338                                    (Set<String>) msg.obj);
1339                            break;
1340
1341                        case ADD_PACKAGE_NAME:
1342                            if (BrowserFrame.sJavaBridge == null) {
1343                                throw new IllegalStateException("No WebView " +
1344                                        "has been created in this process!");
1345                            }
1346                            BrowserFrame.sJavaBridge.addPackageName(
1347                                    (String) msg.obj);
1348                            break;
1349
1350                        case REMOVE_PACKAGE_NAME:
1351                            if (BrowserFrame.sJavaBridge == null) {
1352                                throw new IllegalStateException("No WebView " +
1353                                        "has been created in this process!");
1354                            }
1355                            BrowserFrame.sJavaBridge.removePackageName(
1356                                    (String) msg.obj);
1357                            break;
1358                    }
1359                }
1360            };
1361            // Take all queued messages and resend them to the new handler.
1362            synchronized (this) {
1363                int size = mMessages.size();
1364                for (int i = 0; i < size; i++) {
1365                    mHandler.sendMessage(mMessages.get(i));
1366                }
1367                mMessages = null;
1368            }
1369        }
1370
1371        /**
1372         * Send a message internally to the queue or to the handler
1373         */
1374        private synchronized void sendMessage(Message msg) {
1375            if (mBlockMessages) {
1376                return;
1377            }
1378            if (mMessages != null) {
1379                mMessages.add(msg);
1380            } else {
1381                mHandler.sendMessage(msg);
1382            }
1383        }
1384
1385        private synchronized void removeMessages(int what) {
1386            if (mBlockMessages) {
1387                return;
1388            }
1389            if (what == EventHub.WEBKIT_DRAW) {
1390                mDrawIsScheduled = false;
1391            }
1392            if (mMessages != null) {
1393                Log.w(LOGTAG, "Not supported in this case.");
1394            } else {
1395                mHandler.removeMessages(what);
1396            }
1397        }
1398
1399        private synchronized boolean hasMessages(int what) {
1400            if (mBlockMessages) {
1401                return false;
1402            }
1403            if (mMessages != null) {
1404                Log.w(LOGTAG, "hasMessages() is not supported in this case.");
1405                return false;
1406            } else {
1407                return mHandler.hasMessages(what);
1408            }
1409        }
1410
1411        private synchronized void sendMessageDelayed(Message msg, long delay) {
1412            if (mBlockMessages) {
1413                return;
1414            }
1415            mHandler.sendMessageDelayed(msg, delay);
1416        }
1417
1418        /**
1419         * Send a message internally to the front of the queue.
1420         */
1421        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1422            if (mBlockMessages) {
1423                return;
1424            }
1425            if (mMessages != null) {
1426                mMessages.add(0, msg);
1427            } else {
1428                mHandler.sendMessageAtFrontOfQueue(msg);
1429            }
1430        }
1431
1432        /**
1433         * Remove all the messages.
1434         */
1435        private synchronized void removeMessages() {
1436            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1437            mDrawIsScheduled = false;
1438            mSplitPictureIsScheduled = false;
1439            if (mMessages != null) {
1440                mMessages.clear();
1441            } else {
1442                mHandler.removeCallbacksAndMessages(null);
1443            }
1444        }
1445
1446        /**
1447         * Block sending messages to the EventHub.
1448         */
1449        private synchronized void blockMessages() {
1450            mBlockMessages = true;
1451        }
1452    }
1453
1454    //-------------------------------------------------------------------------
1455    // Methods called by host activity (in the same thread)
1456    //-------------------------------------------------------------------------
1457
1458    void stopLoading() {
1459        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1460        if (mBrowserFrame != null) {
1461            mBrowserFrame.stopLoading();
1462        }
1463    }
1464
1465    //-------------------------------------------------------------------------
1466    // Methods called by WebView
1467    // If it refers to local variable, it needs synchronized().
1468    // If it needs WebCore, it has to send message.
1469    //-------------------------------------------------------------------------
1470
1471    void sendMessage(Message msg) {
1472        mEventHub.sendMessage(msg);
1473    }
1474
1475    void sendMessage(int what) {
1476        mEventHub.sendMessage(Message.obtain(null, what));
1477    }
1478
1479    void sendMessage(int what, Object obj) {
1480        mEventHub.sendMessage(Message.obtain(null, what, obj));
1481    }
1482
1483    void sendMessage(int what, int arg1) {
1484        // just ignore the second argument (make it 0)
1485        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1486    }
1487
1488    void sendMessage(int what, int arg1, int arg2) {
1489        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1490    }
1491
1492    void sendMessage(int what, int arg1, Object obj) {
1493        // just ignore the second argument (make it 0)
1494        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1495    }
1496
1497    void sendMessage(int what, int arg1, int arg2, Object obj) {
1498        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1499    }
1500
1501    void sendMessageAtFrontOfQueue(int what, Object obj) {
1502        mEventHub.sendMessageAtFrontOfQueue(Message.obtain(
1503                null, what, obj));
1504    }
1505
1506    void sendMessageDelayed(int what, Object obj, long delay) {
1507        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1508    }
1509
1510    void removeMessages(int what) {
1511        mEventHub.removeMessages(what);
1512    }
1513
1514    void removeMessages() {
1515        mEventHub.removeMessages();
1516    }
1517
1518    /**
1519     * Removes pending messages and trigger a DESTROY message to send to
1520     * WebCore.
1521     * Called from UI thread.
1522     */
1523    void destroy() {
1524        // We don't want anyone to post a message between removing pending
1525        // messages and sending the destroy message.
1526        synchronized (mEventHub) {
1527            // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
1528            // be preserved even the WebView is destroyed.
1529            // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
1530            boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
1531            boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
1532            mEventHub.removeMessages();
1533            mEventHub.sendMessageAtFrontOfQueue(
1534                    Message.obtain(null, EventHub.DESTROY));
1535            if (hasPause) {
1536                mEventHub.sendMessageAtFrontOfQueue(
1537                        Message.obtain(null, EventHub.PAUSE_TIMERS));
1538            }
1539            if (hasResume) {
1540                mEventHub.sendMessageAtFrontOfQueue(
1541                        Message.obtain(null, EventHub.RESUME_TIMERS));
1542            }
1543            mEventHub.blockMessages();
1544        }
1545    }
1546
1547    //-------------------------------------------------------------------------
1548    // WebViewCore private methods
1549    //-------------------------------------------------------------------------
1550
1551    private void clearCache(boolean includeDiskFiles) {
1552        mBrowserFrame.clearCache();
1553        if (includeDiskFiles) {
1554            CacheManager.removeAllCacheFiles();
1555        }
1556    }
1557
1558    private void loadUrl(String url, Map<String, String> extraHeaders) {
1559        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1560        mBrowserFrame.loadUrl(url, extraHeaders);
1561    }
1562
1563    private void key(KeyEvent evt, boolean isDown) {
1564        if (DebugFlags.WEB_VIEW_CORE) {
1565            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1566                    + evt);
1567        }
1568        int keyCode = evt.getKeyCode();
1569        if (!nativeKey(keyCode, evt.getUnicodeChar(),
1570                evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
1571                evt.isSymPressed(),
1572                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1573            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1574                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1575                if (DebugFlags.WEB_VIEW_CORE) {
1576                    Log.v(LOGTAG, "key: arrow unused by page: " + keyCode);
1577                }
1578                if (mWebView != null && evt.isDown()) {
1579                    Message.obtain(mWebView.mPrivateHandler,
1580                            WebView.UNHANDLED_NAV_KEY, keyCode,
1581                            0).sendToTarget();
1582                }
1583                return;
1584            }
1585            // bubble up the event handling
1586            // but do not bubble up the ENTER key, which would open the search
1587            // bar without any text.
1588            mCallbackProxy.onUnhandledKeyEvent(evt);
1589        }
1590    }
1591
1592    // These values are used to avoid requesting a layout based on old values
1593    private int mCurrentViewWidth = 0;
1594    private int mCurrentViewHeight = 0;
1595    private float mCurrentViewScale = 1.0f;
1596
1597    // notify webkit that our virtual view size changed size (after inv-zoom)
1598    private void viewSizeChanged(int w, int h, int textwrapWidth, float scale,
1599            int anchorX, int anchorY, boolean ignoreHeight) {
1600        if (DebugFlags.WEB_VIEW_CORE) {
1601            Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
1602                    + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
1603        }
1604        if (w == 0) {
1605            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1606            return;
1607        }
1608        int width = w;
1609        if (mSettings.getUseWideViewPort()) {
1610            if (mViewportWidth == -1) {
1611                if (mSettings.getLayoutAlgorithm() ==
1612                        WebSettings.LayoutAlgorithm.NORMAL) {
1613                    width = WebView.DEFAULT_VIEWPORT_WIDTH;
1614                } else {
1615                    /*
1616                     * if a page's minimum preferred width is wider than the
1617                     * given "w", use it instead to get better layout result. If
1618                     * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1619                     * wider. If we start a page with screen width, due to the
1620                     * delay between {@link #didFirstLayout} and
1621                     * {@link #viewSizeChanged},
1622                     * {@link #nativeGetContentMinPrefWidth} will return a more
1623                     * accurate value than initial 0 to result a better layout.
1624                     * In the worse case, the native width will be adjusted when
1625                     * next zoom or screen orientation change happens.
1626                     */
1627                    width = Math.min(WebView.sMaxViewportWidth, Math.max(w,
1628                            Math.max(WebView.DEFAULT_VIEWPORT_WIDTH,
1629                                    nativeGetContentMinPrefWidth())));
1630                }
1631            } else if (mViewportWidth > 0) {
1632                width = Math.max(w, mViewportWidth);
1633            } else {
1634                width = textwrapWidth;
1635            }
1636        }
1637        nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
1638                textwrapWidth, scale, w, h, anchorX, anchorY, ignoreHeight);
1639        // Remember the current width and height
1640        boolean needInvalidate = (mCurrentViewWidth == 0);
1641        mCurrentViewWidth = w;
1642        mCurrentViewHeight = h;
1643        mCurrentViewScale = scale;
1644        if (needInvalidate) {
1645            // ensure {@link #webkitDraw} is called as we were blocking in
1646            // {@link #contentDraw} when mCurrentViewWidth is 0
1647            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1648            contentDraw();
1649        }
1650        mEventHub.sendMessage(Message.obtain(null,
1651                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1652    }
1653
1654    private void sendUpdateTextEntry() {
1655        if (mWebView != null) {
1656            Message.obtain(mWebView.mPrivateHandler,
1657                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1658        }
1659    }
1660
1661    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1662    // callbacks. Computes the sum of database quota for all origins.
1663    private long getUsedQuota() {
1664        WebStorage webStorage = WebStorage.getInstance();
1665        Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
1666
1667        if (origins == null) {
1668            return 0;
1669        }
1670        long usedQuota = 0;
1671        for (WebStorage.Origin website : origins) {
1672            usedQuota += website.getQuota();
1673        }
1674        return usedQuota;
1675    }
1676
1677    // Used to avoid posting more than one draw message.
1678    private boolean mDrawIsScheduled;
1679
1680    // Used to avoid posting more than one split picture message.
1681    private boolean mSplitPictureIsScheduled;
1682
1683    // Used to suspend drawing.
1684    private boolean mDrawIsPaused;
1685
1686    // mRestoreState is set in didFirstLayout(), and reset in the next
1687    // webkitDraw after passing it to the UI thread.
1688    private RestoreState mRestoreState = null;
1689
1690    static class RestoreState {
1691        float mMinScale;
1692        float mMaxScale;
1693        float mViewScale;
1694        float mTextWrapScale;
1695        float mDefaultScale;
1696        int mScrollX;
1697        int mScrollY;
1698        boolean mMobileSite;
1699    }
1700
1701    static class DrawData {
1702        DrawData() {
1703            mInvalRegion = new Region();
1704            mWidthHeight = new Point();
1705        }
1706        Region mInvalRegion;
1707        Point mViewPoint;
1708        Point mWidthHeight;
1709        int mMinPrefWidth;
1710        RestoreState mRestoreState; // only non-null if it is for the first
1711                                    // picture set after the first layout
1712        boolean mFocusSizeChanged;
1713    }
1714
1715    private void webkitDraw() {
1716        mDrawIsScheduled = false;
1717        DrawData draw = new DrawData();
1718        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
1719        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
1720                == false) {
1721            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
1722            return;
1723        }
1724        if (mWebView != null) {
1725            // Send the native view size that was used during the most recent
1726            // layout.
1727            draw.mFocusSizeChanged = nativeFocusBoundsChanged();
1728            draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1729            if (mSettings.getUseWideViewPort()) {
1730                draw.mMinPrefWidth = Math.max(
1731                        mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH
1732                                : (mViewportWidth == 0 ? mCurrentViewWidth
1733                                        : mViewportWidth),
1734                        nativeGetContentMinPrefWidth());
1735            }
1736            if (mRestoreState != null) {
1737                draw.mRestoreState = mRestoreState;
1738                mRestoreState = null;
1739            }
1740            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1741            Message.obtain(mWebView.mPrivateHandler,
1742                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
1743            if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1744                // as we have the new picture, try to sync the scroll position
1745                Message.obtain(mWebView.mPrivateHandler,
1746                        WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1747                        mWebkitScrollY).sendToTarget();
1748                mWebkitScrollX = mWebkitScrollY = 0;
1749            }
1750        }
1751    }
1752
1753    ///////////////////////////////////////////////////////////////////////////
1754    // These are called from the UI thread, not our thread
1755
1756    static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
1757                                         Paint.DITHER_FLAG |
1758                                         Paint.SUBPIXEL_TEXT_FLAG;
1759    static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
1760                                           Paint.DITHER_FLAG;
1761
1762    final DrawFilter mZoomFilter =
1763                    new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
1764    // If we need to trade better quality for speed, set mScrollFilter to null
1765    final DrawFilter mScrollFilter =
1766                new PaintFlagsDrawFilter(SCROLL_BITS, 0);
1767
1768    /* package */ void drawContentPicture(Canvas canvas, int color,
1769                                          boolean animatingZoom,
1770                                          boolean animatingScroll) {
1771        DrawFilter df = null;
1772        if (animatingZoom) {
1773            df = mZoomFilter;
1774        } else if (animatingScroll) {
1775            df = mScrollFilter;
1776        }
1777        canvas.setDrawFilter(df);
1778        boolean tookTooLong = nativeDrawContent(canvas, color);
1779        canvas.setDrawFilter(null);
1780        if (tookTooLong && mSplitPictureIsScheduled == false) {
1781            mSplitPictureIsScheduled = true;
1782            sendMessage(EventHub.SPLIT_PICTURE_SET);
1783        }
1784    }
1785
1786    /* package */ synchronized boolean pictureReady() {
1787        return 0 != mNativeClass ? nativePictureReady() : false;
1788    }
1789
1790    /*package*/ synchronized Picture copyContentPicture() {
1791        Picture result = new Picture();
1792        if (0 != mNativeClass) {
1793            nativeCopyContentToPicture(result);
1794        }
1795        return result;
1796    }
1797
1798    static void reducePriority() {
1799        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1800        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1801        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1802        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1803                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1804    }
1805
1806    static void resumePriority() {
1807        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1808        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1809        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1810        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1811                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1812    }
1813
1814    static void pauseUpdatePicture(WebViewCore core) {
1815        // Note: there is one possible failure mode. If pauseUpdatePicture() is
1816        // called from UI thread while WEBKIT_DRAW is just pulled out of the
1817        // queue in WebCore thread to be executed. Then update won't be blocked.
1818        if (core != null) {
1819            synchronized (core) {
1820                core.mDrawIsPaused = true;
1821                if (core.mDrawIsScheduled) {
1822                    core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1823                }
1824            }
1825        }
1826
1827    }
1828
1829    static void resumeUpdatePicture(WebViewCore core) {
1830        if (core != null) {
1831            synchronized (core) {
1832                core.mDrawIsPaused = false;
1833                if (core.mDrawIsScheduled) {
1834                    core.mDrawIsScheduled = false;
1835                    core.contentDraw();
1836                }
1837            }
1838        }
1839    }
1840
1841    //////////////////////////////////////////////////////////////////////////
1842
1843    private void restoreState(int index) {
1844        WebBackForwardList list = mCallbackProxy.getBackForwardList();
1845        int size = list.getSize();
1846        for (int i = 0; i < size; i++) {
1847            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1848        }
1849        mBrowserFrame.mLoadInitFromJava = true;
1850        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1851        mBrowserFrame.mLoadInitFromJava = false;
1852    }
1853
1854    //-------------------------------------------------------------------------
1855    // Implement abstract methods in WebViewCore, native WebKit callback part
1856    //-------------------------------------------------------------------------
1857
1858    // called from JNI or WebView thread
1859    /* package */ void contentDraw() {
1860        // don't update the Picture until we have an initial width and finish
1861        // the first layout
1862        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1863            return;
1864        }
1865        // only fire an event if this is our first request
1866        synchronized (this) {
1867            if (mDrawIsScheduled) return;
1868            mDrawIsScheduled = true;
1869            if (mDrawIsPaused) return;
1870            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1871        }
1872    }
1873
1874    // called by JNI
1875    private void contentScrollBy(int dx, int dy, boolean animate) {
1876        if (!mBrowserFrame.firstLayoutDone()) {
1877            // Will this happen? If yes, we need to do something here.
1878            return;
1879        }
1880        if (mWebView != null) {
1881            Message msg = Message.obtain(mWebView.mPrivateHandler,
1882                    WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
1883            if (mDrawIsScheduled) {
1884                mEventHub.sendMessage(Message.obtain(null,
1885                        EventHub.MESSAGE_RELAY, msg));
1886            } else {
1887                msg.sendToTarget();
1888            }
1889        }
1890    }
1891
1892    // called by JNI
1893    private void contentScrollTo(int x, int y) {
1894        if (!mBrowserFrame.firstLayoutDone()) {
1895            /*
1896             * WebKit restore state will be called before didFirstLayout(),
1897             * remember the position as it has to be applied after restoring
1898             * zoom factor which is controlled by screenWidth.
1899             */
1900            mRestoredX = x;
1901            mRestoredY = y;
1902            return;
1903        }
1904        if (mWebView != null) {
1905            Message msg = Message.obtain(mWebView.mPrivateHandler,
1906                    WebView.SCROLL_TO_MSG_ID, x, y);
1907            if (mDrawIsScheduled) {
1908                mEventHub.sendMessage(Message.obtain(null,
1909                        EventHub.MESSAGE_RELAY, msg));
1910            } else {
1911                msg.sendToTarget();
1912            }
1913        }
1914    }
1915
1916    // called by JNI
1917    private void contentSpawnScrollTo(int x, int y) {
1918        if (!mBrowserFrame.firstLayoutDone()) {
1919            /*
1920             * WebKit restore state will be called before didFirstLayout(),
1921             * remember the position as it has to be applied after restoring
1922             * zoom factor which is controlled by screenWidth.
1923             */
1924            mRestoredX = x;
1925            mRestoredY = y;
1926            return;
1927        }
1928        if (mWebView != null) {
1929            Message msg = Message.obtain(mWebView.mPrivateHandler,
1930                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
1931            if (mDrawIsScheduled) {
1932                mEventHub.sendMessage(Message.obtain(null,
1933                        EventHub.MESSAGE_RELAY, msg));
1934            } else {
1935                msg.sendToTarget();
1936            }
1937        }
1938    }
1939
1940    // called by JNI
1941    private void sendNotifyProgressFinished() {
1942        sendUpdateTextEntry();
1943        // as CacheManager can behave based on database transaction, we need to
1944        // call tick() to trigger endTransaction
1945        WebViewWorker.getHandler().removeMessages(
1946                WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
1947        WebViewWorker.getHandler().sendEmptyMessage(
1948                WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
1949        contentDraw();
1950    }
1951
1952    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
1953        be scaled before they can be used by the view system, which happens
1954        in WebView since it (and its thread) know the current scale factor.
1955     */
1956    private void sendViewInvalidate(int left, int top, int right, int bottom) {
1957        if (mWebView != null) {
1958            Message.obtain(mWebView.mPrivateHandler,
1959                           WebView.INVAL_RECT_MSG_ID,
1960                           new Rect(left, top, right, bottom)).sendToTarget();
1961        }
1962    }
1963
1964    private static boolean mRepaintScheduled = false;
1965
1966    /*
1967     * Called by the WebView thread
1968     */
1969    /* package */ void signalRepaintDone() {
1970        mRepaintScheduled = false;
1971    }
1972
1973    // called by JNI
1974    private void sendImmediateRepaint() {
1975        if (mWebView != null && !mRepaintScheduled) {
1976            mRepaintScheduled = true;
1977            Message.obtain(mWebView.mPrivateHandler,
1978                           WebView.IMMEDIATE_REPAINT_MSG_ID).sendToTarget();
1979        }
1980    }
1981
1982    // called by JNI
1983    private void setRootLayer(int layer) {
1984        if (mWebView != null) {
1985            Message.obtain(mWebView.mPrivateHandler,
1986                           WebView.SET_ROOT_LAYER_MSG_ID,
1987                           layer, 0).sendToTarget();
1988        }
1989    }
1990
1991    /* package */ WebView getWebView() {
1992        return mWebView;
1993    }
1994
1995    private native void setViewportSettingsFromNative();
1996
1997    // called by JNI
1998    private void didFirstLayout(boolean standardLoad) {
1999        if (DebugFlags.WEB_VIEW_CORE) {
2000            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
2001        }
2002
2003        mBrowserFrame.didFirstLayout();
2004
2005        if (mWebView == null) return;
2006
2007        boolean updateRestoreState = standardLoad || mRestoredScale > 0;
2008        setupViewport(updateRestoreState);
2009        // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will
2010        // be called after the WebView restore the state. If updateRestoreState
2011        // is false, start to draw now as it is ready.
2012        if (!updateRestoreState) {
2013            mWebView.mViewManager.postReadyToDrawAll();
2014        }
2015
2016        // reset the scroll position, the restored offset and scales
2017        mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY
2018                = mRestoredScale = mRestoredScreenWidthScale = 0;
2019    }
2020
2021    // called by JNI
2022    private void updateViewport() {
2023        // if updateViewport is called before first layout, wait until first
2024        // layout to update the viewport. In the rare case, this is called after
2025        // first layout, force an update as we have just parsed the viewport
2026        // meta tag.
2027        if (mBrowserFrame.firstLayoutDone()) {
2028            setupViewport(true);
2029        }
2030    }
2031
2032    private void setupViewport(boolean updateRestoreState) {
2033        // set the viewport settings from WebKit
2034        setViewportSettingsFromNative();
2035
2036        // adjust the default scale to match the densityDpi
2037        float adjust = 1.0f;
2038        if (mViewportDensityDpi == -1) {
2039            if (WebView.DEFAULT_SCALE_PERCENT != 100) {
2040                adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f;
2041            }
2042        } else if (mViewportDensityDpi > 0) {
2043            adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
2044                    / mViewportDensityDpi;
2045        }
2046        int defaultScale = (int) (adjust * 100);
2047
2048        if (mViewportInitialScale > 0) {
2049            mViewportInitialScale *= adjust;
2050        }
2051        if (mViewportMinimumScale > 0) {
2052            mViewportMinimumScale *= adjust;
2053        }
2054        if (mViewportMaximumScale > 0) {
2055            mViewportMaximumScale *= adjust;
2056        }
2057
2058        // infer the values if they are not defined.
2059        if (mViewportWidth == 0) {
2060            if (mViewportInitialScale == 0) {
2061                mViewportInitialScale = defaultScale;
2062            }
2063        }
2064        if (mViewportUserScalable == false) {
2065            mViewportInitialScale = defaultScale;
2066            mViewportMinimumScale = defaultScale;
2067            mViewportMaximumScale = defaultScale;
2068        }
2069        if (mViewportMinimumScale > mViewportInitialScale
2070                && mViewportInitialScale != 0) {
2071            mViewportMinimumScale = mViewportInitialScale;
2072        }
2073        if (mViewportMaximumScale > 0
2074                && mViewportMaximumScale < mViewportInitialScale) {
2075            mViewportMaximumScale = mViewportInitialScale;
2076        }
2077        if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
2078            mViewportWidth = 0;
2079        }
2080
2081        // if mViewportWidth is 0, it means device-width, always update.
2082        if (mViewportWidth != 0 && !updateRestoreState) {
2083            RestoreState restoreState = new RestoreState();
2084            restoreState.mMinScale = mViewportMinimumScale / 100.0f;
2085            restoreState.mMaxScale = mViewportMaximumScale / 100.0f;
2086            restoreState.mDefaultScale = adjust;
2087            // as mViewportWidth is not 0, it is not mobile site.
2088            restoreState.mMobileSite = false;
2089            // for non-mobile site, we don't need minPrefWidth, set it as 0
2090            restoreState.mScrollX = 0;
2091            Message.obtain(mWebView.mPrivateHandler,
2092                    WebView.UPDATE_ZOOM_RANGE, restoreState).sendToTarget();
2093            return;
2094        }
2095
2096        // now notify webview
2097        // webViewWidth refers to the width in the view system
2098        int webViewWidth;
2099        // viewportWidth refers to the width in the document system
2100        int viewportWidth = mCurrentViewWidth;
2101        if (viewportWidth == 0) {
2102            // this may happen when WebView just starts. This is not perfect as
2103            // we call WebView method from WebCore thread. But not perfect
2104            // reference is better than no reference.
2105            webViewWidth = mWebView.getViewWidth();
2106            viewportWidth = (int) (webViewWidth / adjust);
2107            if (viewportWidth == 0) {
2108                Log.w(LOGTAG, "Can't get the viewWidth after the first layout");
2109            }
2110        } else {
2111            webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
2112        }
2113        mRestoreState = new RestoreState();
2114        mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
2115        mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
2116        mRestoreState.mDefaultScale = adjust;
2117        mRestoreState.mScrollX = mRestoredX;
2118        mRestoreState.mScrollY = mRestoredY;
2119        mRestoreState.mMobileSite = (0 == mViewportWidth);
2120        if (mRestoredScale > 0) {
2121            mRestoreState.mViewScale = mRestoredScale / 100.0f;
2122            if (mRestoredScreenWidthScale > 0) {
2123                mRestoreState.mTextWrapScale =
2124                        mRestoredScreenWidthScale / 100.0f;
2125            } else {
2126                mRestoreState.mTextWrapScale = mRestoreState.mViewScale;
2127            }
2128        } else {
2129            if (mViewportInitialScale > 0) {
2130                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
2131                        mViewportInitialScale / 100.0f;
2132            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
2133                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
2134                        (float) webViewWidth / mViewportWidth;
2135            } else {
2136                mRestoreState.mTextWrapScale = adjust;
2137                // 0 will trigger WebView to turn on zoom overview mode
2138                mRestoreState.mViewScale = 0;
2139            }
2140        }
2141
2142        if (mWebView.mHeightCanMeasure) {
2143            // Trick to ensure that the Picture has the exact height for the
2144            // content by forcing to layout with 0 height after the page is
2145            // ready, which is indicated by didFirstLayout. This is essential to
2146            // get rid of the white space in the GMail which uses WebView for
2147            // message view.
2148            mWebView.mLastHeightSent = 0;
2149            // Send a negative scale to indicate that WebCore should reuse
2150            // the current scale
2151            WebView.ViewSizeData data = new WebView.ViewSizeData();
2152            data.mWidth = mWebView.mLastWidthSent;
2153            data.mHeight = 0;
2154            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
2155            // true. It is safe to use mWidth for mTextWrapWidth.
2156            data.mTextWrapWidth = data.mWidth;
2157            data.mScale = -1.0f;
2158            data.mIgnoreHeight = false;
2159            data.mAnchorX = data.mAnchorY = 0;
2160            // send VIEW_SIZE_CHANGED to the front of the queue so that we can
2161            // avoid pushing the wrong picture to the WebView side. If there is
2162            // a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
2163            // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED
2164            // in the queue, as mLastHeightSent has been updated here, we may
2165            // miss the requestLayout in WebView side after the new picture.
2166            mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2167            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2168                    EventHub.VIEW_SIZE_CHANGED, data));
2169        } else if (mSettings.getUseWideViewPort()) {
2170            if (viewportWidth == 0) {
2171                // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
2172                // to WebViewCore
2173                mWebView.mLastWidthSent = 0;
2174            } else {
2175                WebView.ViewSizeData data = new WebView.ViewSizeData();
2176                // mViewScale as 0 means it is in zoom overview mode. So we don't
2177                // know the exact scale. If mRestoredScale is non-zero, use it;
2178                // otherwise just use mTextWrapScale as the initial scale.
2179                data.mScale = mRestoreState.mViewScale == 0
2180                        ? (mRestoredScale > 0 ? mRestoredScale / 100.0f
2181                                : mRestoreState.mTextWrapScale)
2182                        : mRestoreState.mViewScale;
2183                if (DebugFlags.WEB_VIEW_CORE) {
2184                    Log.v(LOGTAG, "setupViewport"
2185                             + " mRestoredScale=" + mRestoredScale
2186                             + " mViewScale=" + mRestoreState.mViewScale
2187                             + " mTextWrapScale=" + mRestoreState.mTextWrapScale
2188                             );
2189                }
2190                data.mWidth = Math.round(webViewWidth / data.mScale);
2191                // We may get a call here when mCurrentViewHeight == 0 if webcore completes the
2192                // first layout before we sync our webview dimensions to it. In that case, we
2193                // request the real height of the webview. This is not a perfect solution as we
2194                // are calling a WebView method from the WebCore thread. But this is preferable
2195                // to syncing an incorrect height.
2196                data.mHeight = mCurrentViewHeight == 0 ?
2197                        Math.round(mWebView.getViewHeight() / data.mScale)
2198                        : mCurrentViewHeight * data.mWidth / viewportWidth;
2199                data.mTextWrapWidth = Math.round(webViewWidth
2200                        / mRestoreState.mTextWrapScale);
2201                data.mIgnoreHeight = false;
2202                data.mAnchorX = data.mAnchorY = 0;
2203                // send VIEW_SIZE_CHANGED to the front of the queue so that we
2204                // can avoid pushing the wrong picture to the WebView side.
2205                mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2206                mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2207                        EventHub.VIEW_SIZE_CHANGED, data));
2208            }
2209        }
2210    }
2211
2212    // called by JNI
2213    private void restoreScale(int scale) {
2214        if (mBrowserFrame.firstLayoutDone() == false) {
2215            mRestoredScale = scale;
2216        }
2217    }
2218
2219    // called by JNI
2220    private void restoreScreenWidthScale(int scale) {
2221        if (!mSettings.getUseWideViewPort()) {
2222            return;
2223        }
2224
2225        if (mBrowserFrame.firstLayoutDone() == false) {
2226            mRestoredScreenWidthScale = scale;
2227        }
2228    }
2229
2230    // called by JNI
2231    private void needTouchEvents(boolean need) {
2232        if (mWebView != null) {
2233            Message.obtain(mWebView.mPrivateHandler,
2234                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
2235                    .sendToTarget();
2236        }
2237    }
2238
2239    // called by JNI
2240    private void updateTextfield(int ptr, boolean changeToPassword,
2241            String text, int textGeneration) {
2242        if (mWebView != null) {
2243            Message msg = Message.obtain(mWebView.mPrivateHandler,
2244                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
2245                    textGeneration, text);
2246            msg.getData().putBoolean("password", changeToPassword);
2247            msg.sendToTarget();
2248        }
2249    }
2250
2251    // called by JNI
2252    private void updateTextSelection(int pointer, int start, int end,
2253            int textGeneration) {
2254        if (mWebView != null) {
2255            Message.obtain(mWebView.mPrivateHandler,
2256                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
2257                new TextSelectionData(start, end)).sendToTarget();
2258        }
2259    }
2260
2261    // called by JNI
2262    private void clearTextEntry() {
2263        if (mWebView == null) return;
2264        Message.obtain(mWebView.mPrivateHandler,
2265                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
2266    }
2267
2268    // called by JNI
2269    private void sendFindAgain() {
2270        if (mWebView == null) return;
2271        Message.obtain(mWebView.mPrivateHandler,
2272                WebView.FIND_AGAIN).sendToTarget();
2273    }
2274
2275    private native void nativeUpdateFrameCacheIfLoading();
2276    private native String nativeRequestLabel(int framePtr, int nodePtr);
2277    /**
2278     * Scroll the focused textfield to (xPercent, y) in document space
2279     */
2280    private native void nativeScrollFocusedTextInput(float xPercent, int y);
2281
2282    // these must be in document space (i.e. not scaled/zoomed).
2283    private native void nativeSetScrollOffset(int gen, int dx, int dy);
2284
2285    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
2286
2287    // called by JNI
2288    private void requestListBox(String[] array, int[] enabledArray,
2289            int[] selectedArray) {
2290        if (mWebView != null) {
2291            mWebView.requestListBox(array, enabledArray, selectedArray);
2292        }
2293    }
2294
2295    // called by JNI
2296    private void requestListBox(String[] array, int[] enabledArray,
2297            int selection) {
2298        if (mWebView != null) {
2299            mWebView.requestListBox(array, enabledArray, selection);
2300        }
2301
2302    }
2303
2304    // called by JNI
2305    private void requestKeyboardWithSelection(int pointer, int selStart,
2306            int selEnd, int textGeneration) {
2307        if (mWebView != null) {
2308            Message.obtain(mWebView.mPrivateHandler,
2309                    WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
2310                    textGeneration, new TextSelectionData(selStart, selEnd))
2311                    .sendToTarget();
2312        }
2313    }
2314
2315    // called by JNI
2316    private void requestKeyboard(boolean showKeyboard) {
2317        if (mWebView != null) {
2318            Message.obtain(mWebView.mPrivateHandler,
2319                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
2320                    .sendToTarget();
2321        }
2322    }
2323
2324    // called by JNI
2325    private Context getContext() {
2326        return mContext;
2327    }
2328
2329    // called by JNI
2330    private Class<?> getPluginClass(String libName, String clsName) {
2331
2332        if (mWebView == null) {
2333            return null;
2334        }
2335
2336        PluginManager pluginManager = PluginManager.getInstance(null);
2337
2338        String pkgName = pluginManager.getPluginsAPKName(libName);
2339        if (pkgName == null) {
2340            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2341            return null;
2342        }
2343
2344        try {
2345            return pluginManager.getPluginClass(pkgName, clsName);
2346        } catch (NameNotFoundException e) {
2347            Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")");
2348        } catch (ClassNotFoundException e) {
2349            Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
2350                    ") in the apk (" + pkgName + ")");
2351        }
2352
2353        return null;
2354    }
2355
2356    // called by JNI. PluginWidget function to launch a full-screen view using a
2357    // View object provided by the plugin class.
2358    private void showFullScreenPlugin(ViewManager.ChildView childView, int npp) {
2359        if (mWebView == null) {
2360            return;
2361        }
2362
2363        Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN);
2364        message.obj = childView.mView;
2365        message.arg1 = npp;
2366        message.sendToTarget();
2367    }
2368
2369    // called by JNI
2370    private void hideFullScreenPlugin() {
2371        if (mWebView == null) {
2372            return;
2373        }
2374        mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
2375                .sendToTarget();
2376    }
2377
2378    // called by JNI.  PluginWidget functions for creating an embedded View for
2379    // the surface drawing model.
2380    private ViewManager.ChildView addSurface(View pluginView, int x, int y,
2381                                             int width, int height) {
2382        if (mWebView == null) {
2383            return null;
2384        }
2385
2386        if (pluginView == null) {
2387            Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy");
2388            return null;
2389        }
2390
2391        // ensures the view system knows the view can redraw itself
2392        pluginView.setWillNotDraw(false);
2393
2394        if(pluginView instanceof SurfaceView)
2395            ((SurfaceView)pluginView).setZOrderOnTop(true);
2396
2397        ViewManager.ChildView view = mWebView.mViewManager.createView();
2398        view.mView = pluginView;
2399        view.attachView(x, y, width, height);
2400        return view;
2401    }
2402
2403    private void updateSurface(ViewManager.ChildView childView, int x, int y,
2404            int width, int height) {
2405        childView.attachView(x, y, width, height);
2406    }
2407
2408    private void destroySurface(ViewManager.ChildView childView) {
2409        childView.removeView();
2410    }
2411
2412    // called by JNI
2413    static class ShowRectData {
2414        int mLeft;
2415        int mTop;
2416        int mWidth;
2417        int mHeight;
2418        int mContentWidth;
2419        int mContentHeight;
2420        float mXPercentInDoc;
2421        float mXPercentInView;
2422        float mYPercentInDoc;
2423        float mYPercentInView;
2424    }
2425
2426    private void showRect(int left, int top, int width, int height,
2427            int contentWidth, int contentHeight, float xPercentInDoc,
2428            float xPercentInView, float yPercentInDoc, float yPercentInView) {
2429        if (mWebView != null) {
2430            ShowRectData data = new ShowRectData();
2431            data.mLeft = left;
2432            data.mTop = top;
2433            data.mWidth = width;
2434            data.mHeight = height;
2435            data.mContentWidth = contentWidth;
2436            data.mContentHeight = contentHeight;
2437            data.mXPercentInDoc = xPercentInDoc;
2438            data.mXPercentInView = xPercentInView;
2439            data.mYPercentInDoc = yPercentInDoc;
2440            data.mYPercentInView = yPercentInView;
2441            Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID,
2442                    data).sendToTarget();
2443        }
2444    }
2445
2446    // called by JNI
2447    private void centerFitRect(int x, int y, int width, int height) {
2448        if (mWebView == null) {
2449            return;
2450        }
2451        mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT,
2452                new Rect(x, y, x + width, y + height)).sendToTarget();
2453    }
2454
2455    // called by JNI
2456    private void setScrollbarModes(int hMode, int vMode) {
2457        if (mWebView == null) {
2458            return;
2459        }
2460        mWebView.mPrivateHandler.obtainMessage(WebView.SET_SCROLLBAR_MODES,
2461                hMode, vMode).sendToTarget();
2462    }
2463
2464    private native void nativePause();
2465    private native void nativeResume();
2466    private native void nativeFreeMemory();
2467    private native void nativeFullScreenPluginHidden(int npp);
2468    private native boolean nativeValidNodeAndBounds(int frame, int node,
2469            Rect bounds);
2470
2471}
2472