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