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