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