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