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