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