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