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