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