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