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