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