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