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