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