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