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