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