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