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