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