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