WebViewCore.java revision fa32db15a06d952537af046dddf083757de10b6b
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.graphics.Canvas;
21import android.graphics.DrawFilter;
22import android.graphics.Paint;
23import android.graphics.PaintFlagsDrawFilter;
24import android.graphics.Picture;
25import android.graphics.Point;
26import android.graphics.Rect;
27import android.graphics.Region;
28import android.os.Handler;
29import android.os.Looper;
30import android.os.Message;
31import android.os.Process;
32import android.util.Log;
33import android.util.SparseBooleanArray;
34import android.view.KeyEvent;
35import android.view.SurfaceHolder;
36import android.view.SurfaceView;
37
38import java.util.ArrayList;
39import java.util.Set;
40
41import junit.framework.Assert;
42
43final class WebViewCore {
44
45    private static final String LOGTAG = "webcore";
46
47    static {
48        // Load libwebcore during static initialization. This happens in the
49        // zygote process so it will be shared read-only across all app
50        // processes.
51        System.loadLibrary("webcore");
52    }
53
54    /*
55     * WebViewCore always executes in the same thread as the native webkit.
56     */
57
58    // The WebView that corresponds to this WebViewCore.
59    private WebView mWebView;
60    // Proxy for handling callbacks from native code
61    private final CallbackProxy mCallbackProxy;
62    // Settings object for maintaining all settings
63    private final WebSettings mSettings;
64    // Context for initializing the BrowserFrame with the proper assets.
65    private final Context mContext;
66    // The pointer to a native view object.
67    private int mNativeClass;
68    // The BrowserFrame is an interface to the native Frame component.
69    private BrowserFrame mBrowserFrame;
70
71    /*
72     * range is from 200 to 10,000. 0 is a special value means device-width. -1
73     * means undefined.
74     */
75    private int mViewportWidth = -1;
76
77    /*
78     * range is from 200 to 10,000. 0 is a special value means device-height. -1
79     * means undefined.
80     */
81    private int mViewportHeight = -1;
82
83    /*
84     * scale in percent, range is from 1 to 1000. 0 means undefined.
85     */
86    private int mViewportInitialScale = 0;
87
88    /*
89     * scale in percent, range is from 1 to 1000. 0 means undefined.
90     */
91    private int mViewportMinimumScale = 0;
92
93    /*
94     * scale in percent, range is from 1 to 1000. 0 means undefined.
95     */
96    private int mViewportMaximumScale = 0;
97
98    private boolean mViewportUserScalable = true;
99
100    private int mRestoredScale = 0;
101    private int mRestoredScreenWidthScale = 0;
102    private int mRestoredX = 0;
103    private int mRestoredY = 0;
104
105    private int mWebkitScrollX = 0;
106    private int mWebkitScrollY = 0;
107
108    // If the site doesn't use viewport meta tag to specify the viewport, use
109    // DEFAULT_VIEWPORT_WIDTH as default viewport width
110    static final int DEFAULT_VIEWPORT_WIDTH = 800;
111
112    // The thread name used to identify the WebCore thread and for use in
113    // debugging other classes that require operation within the WebCore thread.
114    /* package */ static final String THREAD_NAME = "WebViewCoreThread";
115
116    public WebViewCore(Context context, WebView w, CallbackProxy proxy) {
117        // No need to assign this in the WebCore thread.
118        mCallbackProxy = proxy;
119        mWebView = w;
120        // This context object is used to initialize the WebViewCore during
121        // subwindow creation.
122        mContext = context;
123
124        // We need to wait for the initial thread creation before sending
125        // a message to the WebCore thread.
126        // XXX: This is the only time the UI thread will wait for the WebCore
127        // thread!
128        synchronized (WebViewCore.class) {
129            if (sWebCoreHandler == null) {
130                // Create a global thread and start it.
131                Thread t = new Thread(new WebCoreThread());
132                t.setName(THREAD_NAME);
133                t.start();
134                try {
135                    WebViewCore.class.wait();
136                } catch (InterruptedException e) {
137                    Log.e(LOGTAG, "Caught exception while waiting for thread " +
138                           "creation.");
139                    Log.e(LOGTAG, Log.getStackTraceString(e));
140                }
141            }
142        }
143        // Create an EventHub to handle messages before and after the thread is
144        // ready.
145        mEventHub = new EventHub();
146        // Create a WebSettings object for maintaining all settings
147        mSettings = new WebSettings(mContext, mWebView);
148        // The WebIconDatabase needs to be initialized within the UI thread so
149        // just request the instance here.
150        WebIconDatabase.getInstance();
151        // Create the WebStorage singleton
152        WebStorage.getInstance();
153        // Send a message to initialize the WebViewCore.
154        Message init = sWebCoreHandler.obtainMessage(
155                WebCoreThread.INITIALIZE, this);
156        sWebCoreHandler.sendMessage(init);
157    }
158
159    /* Initialize private data within the WebCore thread.
160     */
161    private void initialize() {
162        /* Initialize our private BrowserFrame class to handle all
163         * frame-related functions. We need to create a new view which
164         * in turn creates a C level FrameView and attaches it to the frame.
165         */
166        mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy,
167                mSettings);
168        // Sync the native settings and also create the WebCore thread handler.
169        mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
170        // Create the handler and transfer messages for the IconDatabase
171        WebIconDatabase.getInstance().createHandler();
172        // Create the handler for WebStorage
173        WebStorage.getInstance().createHandler();
174        // Create the handler for GeolocationPermissions.
175        GeolocationPermissions.getInstance().createHandler();
176        // The transferMessages call will transfer all pending messages to the
177        // WebCore thread handler.
178        mEventHub.transferMessages();
179
180        // Send a message back to WebView to tell it that we have set up the
181        // WebCore thread.
182        if (mWebView != null) {
183            Message.obtain(mWebView.mPrivateHandler,
184                    WebView.WEBCORE_INITIALIZED_MSG_ID,
185                    mNativeClass, 0).sendToTarget();
186        }
187
188    }
189
190    /* Handle the initialization of WebViewCore during subwindow creation. This
191     * method is called from the WebCore thread but it is called before the
192     * INITIALIZE message can be handled.
193     */
194    /* package */ void initializeSubwindow() {
195        // Go ahead and initialize the core components.
196        initialize();
197        // Remove the INITIALIZE method so we don't try to initialize twice.
198        sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this);
199    }
200
201    /* Get the BrowserFrame component. This is used for subwindow creation and
202     * is called only from BrowserFrame in the WebCore thread. */
203    /* package */ BrowserFrame getBrowserFrame() {
204        return mBrowserFrame;
205    }
206
207    //-------------------------------------------------------------------------
208    // Common methods
209    //-------------------------------------------------------------------------
210
211    /**
212     * Causes all timers to pause. This applies to all WebViews in the current
213     * app process.
214     */
215    public static void pauseTimers() {
216        if (BrowserFrame.sJavaBridge == null) {
217            throw new IllegalStateException(
218                    "No WebView has been created in this process!");
219        }
220        BrowserFrame.sJavaBridge.pause();
221    }
222
223    /**
224     * Resume all timers. This applies to all WebViews in the current process.
225     */
226    public static void resumeTimers() {
227        if (BrowserFrame.sJavaBridge == null) {
228            throw new IllegalStateException(
229                    "No WebView has been created in this process!");
230        }
231        BrowserFrame.sJavaBridge.resume();
232    }
233
234    public WebSettings getSettings() {
235        return mSettings;
236    }
237
238    /**
239     * Add an error message to the client's console.
240     * @param message The message to add
241     * @param lineNumber the line on which the error occurred
242     * @param sourceID the filename of the source that caused the error.
243     */
244    protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
245        mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
246    }
247
248    /**
249     * Invoke a javascript alert.
250     * @param message The message displayed in the alert.
251     */
252    protected void jsAlert(String url, String message) {
253        mCallbackProxy.onJsAlert(url, message);
254    }
255
256    /**
257     * Notify the browser that the origin has exceeded it's database quota.
258     * @param url The URL that caused the overflow.
259     * @param databaseIdentifier The identifier of the database.
260     * @param currentQuota The current quota for the origin.
261     */
262    protected void exceededDatabaseQuota(String url,
263                                         String databaseIdentifier,
264                                         long currentQuota) {
265        // Inform the callback proxy of the quota overflow. Send an object
266        // that encapsulates a call to the nativeSetDatabaseQuota method to
267        // awaken the sleeping webcore thread when a decision from the
268        // client to allow or deny quota is available.
269        mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
270                currentQuota, getUsedQuota(), new WebStorage.QuotaUpdater() {
271                                  public void updateQuota(long quota) {
272                                      nativeSetNewStorageLimit(quota);
273                                  }
274                              });
275    }
276
277    /**
278     * Notify the browser that the appcache has exceeded its max size.
279     * @param spaceNeeded is the amount of disk space that would be needed
280     * in order for the last appcache operation to succeed.
281     */
282    protected void reachedMaxAppCacheSize(long spaceNeeded) {
283        mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(),
284                new WebStorage.QuotaUpdater() {
285                    public void updateQuota(long quota) {
286                        nativeSetNewStorageLimit(quota);
287                    }
288                });
289    }
290
291    /**
292     * Shows a prompt to ask the user to set the Geolocation permission state
293     * for the given origin.
294     * @param origin The origin for which Geolocation permissions are
295     *     requested.
296     */
297    protected void geolocationPermissionsShowPrompt(String origin) {
298        mCallbackProxy.onGeolocationPermissionsShowPrompt(origin,
299                new GeolocationPermissions.Callback() {
300          public void invoke(String origin, boolean allow, boolean remember) {
301            GeolocationPermissionsData data = new GeolocationPermissionsData();
302            data.mOrigin = origin;
303            data.mAllow = allow;
304            data.mRemember = remember;
305            // Marshall to WebCore thread.
306            sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data);
307          }
308        });
309    }
310
311    /**
312     * Hides the Geolocation permissions prompt.
313     */
314    protected void geolocationPermissionsHidePrompt() {
315        mCallbackProxy.onGeolocationPermissionsHidePrompt();
316    }
317
318    /**
319     * Invoke a javascript confirm dialog.
320     * @param message The message displayed in the dialog.
321     * @return True if the user confirmed or false if the user cancelled.
322     */
323    protected boolean jsConfirm(String url, String message) {
324        return mCallbackProxy.onJsConfirm(url, message);
325    }
326
327    /**
328     * Invoke a javascript prompt dialog.
329     * @param message The message to be displayed in the dialog.
330     * @param defaultValue The default value in the prompt input.
331     * @return The input from the user or null to indicate the user cancelled
332     *         the dialog.
333     */
334    protected String jsPrompt(String url, String message, String defaultValue) {
335        return mCallbackProxy.onJsPrompt(url, message, defaultValue);
336    }
337
338    /**
339     * Invoke a javascript before unload dialog.
340     * @param url The url that is requesting the dialog.
341     * @param message The message displayed in the dialog.
342     * @return True if the user confirmed or false if the user cancelled. False
343     *         will cancel the navigation.
344     */
345    protected boolean jsUnload(String url, String message) {
346        return mCallbackProxy.onJsBeforeUnload(url, message);
347    }
348
349    /**
350     *
351     * Callback to notify that a JavaScript execution timeout has occured.
352     * @return True if the JavaScript execution should be interrupted. False
353     *         will continue the execution.
354     */
355    protected boolean jsInterrupt() {
356        return mCallbackProxy.onJsTimeout();
357    }
358
359    //-------------------------------------------------------------------------
360    // JNI methods
361    //-------------------------------------------------------------------------
362
363    static native String nativeFindAddress(String addr, boolean caseInsensitive);
364
365    /**
366     * Empty the picture set.
367     */
368    private native void nativeClearContent();
369
370    /**
371     * Create a flat picture from the set of pictures.
372     */
373    private native void nativeCopyContentToPicture(Picture picture);
374
375    /**
376     * Draw the picture set with a background color. Returns true
377     * if some individual picture took too long to draw and can be
378     * split into parts. Called from the UI thread.
379     */
380    private native boolean nativeDrawContent(Canvas canvas, int color);
381
382    /**
383     * check to see if picture is blank and in progress
384     */
385    private native boolean nativePictureReady();
386
387    /**
388     * Redraw a portion of the picture set. The Point wh returns the
389     * width and height of the overall picture.
390     */
391    private native boolean nativeRecordContent(Region invalRegion, Point wh);
392
393    /**
394     * Splits slow parts of the picture set. Called from the webkit
395     * thread after nativeDrawContent returns true.
396     */
397    private native void nativeSplitContent();
398
399    private native boolean nativeKey(int keyCode, int unichar,
400            int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
401            boolean isDown);
402
403    private native void nativeClick(int framePtr, int nodePtr);
404
405    private native void nativeSendListBoxChoices(boolean[] choices, int size);
406
407    private native void nativeSendListBoxChoice(int choice);
408
409    /*  Tell webkit what its width and height are, for the purposes
410        of layout/line-breaking. These coordinates are in document space,
411        which is the same as View coords unless we have zoomed the document
412        (see nativeSetZoom).
413        screenWidth is used by layout to wrap column around. If viewport uses
414        fixed size, screenWidth can be different from width with zooming.
415        should this be called nativeSetViewPortSize?
416    */
417    private native void nativeSetSize(int width, int height, int screenWidth,
418            float scale, int realScreenWidth, int screenHeight);
419
420    private native int nativeGetContentMinPrefWidth();
421
422    // Start: functions that deal with text editing
423    private native void nativeReplaceTextfieldText(
424            int oldStart, int oldEnd, String replace, int newStart, int newEnd,
425            int textGeneration);
426
427    private native void passToJs(int gen,
428            String currentText, int keyCode, int keyValue, boolean down,
429            boolean cap, boolean fn, boolean sym);
430
431    private native void nativeSetFocusControllerActive(boolean active);
432
433    private native void nativeSaveDocumentState(int frame);
434
435    private native void nativeMoveMouse(int framePtr, int x, int y);
436
437    private native void nativeMoveMouseIfLatest(int moveGeneration,
438            int framePtr, int x, int y);
439
440    private native String nativeRetrieveHref(int framePtr, int nodePtr);
441
442    private native void nativeTouchUp(int touchGeneration,
443            int framePtr, int nodePtr, int x, int y,
444            int size);
445
446    private native boolean nativeHandleTouchEvent(int action, int x, int y);
447
448    private native void nativeUpdateFrameCache();
449
450    private native void nativeSetBackgroundColor(int color);
451
452    private native void nativeDumpDomTree(boolean useFile);
453
454    private native void nativeDumpRenderTree(boolean useFile);
455
456    private native void nativeDumpNavTree();
457
458    private native void nativeSetJsFlags(String flags);
459
460    /**
461     *  Delete text from start to end in the focused textfield. If there is no
462     *  focus, or if start == end, silently fail.  If start and end are out of
463     *  order, swap them.
464     *  @param  start   Beginning of selection to delete.
465     *  @param  end     End of selection to delete.
466     *  @param  textGeneration Text generation number when delete was pressed.
467     */
468    private native void nativeDeleteSelection(int start, int end,
469            int textGeneration);
470
471    /**
472     *  Set the selection to (start, end) in the focused textfield. If start and
473     *  end are out of order, swap them.
474     *  @param  start   Beginning of selection.
475     *  @param  end     End of selection.
476     */
477    private native void nativeSetSelection(int start, int end);
478
479    private native String nativeGetSelection(Region sel);
480
481    // Register a scheme to be treated as local scheme so that it can access
482    // local asset files for resources
483    private native void nativeRegisterURLSchemeAsLocal(String scheme);
484
485    /*
486     * Inform webcore that the user has decided whether to allow or deny new
487     * quota for the current origin or more space for the app cache, and that
488     * the main thread should wake up now.
489     * @param limit Is the new quota for an origin or new app cache max size.
490     */
491    private native void nativeSetNewStorageLimit(long limit);
492
493    private native void nativeUpdatePluginState(int framePtr, int nodePtr, int state);
494
495    /**
496     * Provide WebCore with a Geolocation permission state for the specified
497     * origin.
498     * @param origin The origin for which Geolocation permissions are provided.
499     * @param allow Whether Geolocation permissions are allowed.
500     * @param remember Whether this decision should be remembered beyond the
501     *     life of the current page.
502     */
503    private native void nativeGeolocationPermissionsProvide(String origin, boolean allow, boolean remember);
504
505    // EventHub for processing messages
506    private final EventHub mEventHub;
507    // WebCore thread handler
508    private static Handler sWebCoreHandler;
509    // Class for providing Handler creation inside the WebCore thread.
510    private static class WebCoreThread implements Runnable {
511        // Message id for initializing a new WebViewCore.
512        private static final int INITIALIZE = 0;
513        private static final int REDUCE_PRIORITY = 1;
514        private static final int RESUME_PRIORITY = 2;
515        private static final int CACHE_TICKER = 3;
516        private static final int BLOCK_CACHE_TICKER = 4;
517        private static final int RESUME_CACHE_TICKER = 5;
518
519        private static final int CACHE_TICKER_INTERVAL = 60 * 1000; // 1 minute
520
521        private static boolean mCacheTickersBlocked = true;
522
523        public void run() {
524            Looper.prepare();
525            Assert.assertNull(sWebCoreHandler);
526            synchronized (WebViewCore.class) {
527                sWebCoreHandler = new Handler() {
528                    @Override
529                    public void handleMessage(Message msg) {
530                        switch (msg.what) {
531                            case INITIALIZE:
532                                WebViewCore core = (WebViewCore) msg.obj;
533                                core.initialize();
534                                break;
535
536                            case REDUCE_PRIORITY:
537                                // 3 is an adjustable number.
538                                Process.setThreadPriority(
539                                        Process.THREAD_PRIORITY_DEFAULT + 3 *
540                                        Process.THREAD_PRIORITY_LESS_FAVORABLE);
541                                break;
542
543                            case RESUME_PRIORITY:
544                                Process.setThreadPriority(
545                                        Process.THREAD_PRIORITY_DEFAULT);
546                                break;
547
548                            case CACHE_TICKER:
549                                if (!mCacheTickersBlocked) {
550                                    CacheManager.endCacheTransaction();
551                                    CacheManager.startCacheTransaction();
552                                    sendMessageDelayed(
553                                            obtainMessage(CACHE_TICKER),
554                                            CACHE_TICKER_INTERVAL);
555                                }
556                                break;
557
558                            case BLOCK_CACHE_TICKER:
559                                if (CacheManager.endCacheTransaction()) {
560                                    mCacheTickersBlocked = true;
561                                }
562                                break;
563
564                            case RESUME_CACHE_TICKER:
565                                if (CacheManager.startCacheTransaction()) {
566                                    mCacheTickersBlocked = false;
567                                }
568                                break;
569                        }
570                    }
571                };
572                WebViewCore.class.notify();
573            }
574            Looper.loop();
575        }
576    }
577
578    static class BaseUrlData {
579        String mBaseUrl;
580        String mData;
581        String mMimeType;
582        String mEncoding;
583        String mFailUrl;
584    }
585
586    static class CursorData {
587        CursorData() {}
588        CursorData(int frame, int node, int x, int y) {
589            mFrame = frame;
590            mX = x;
591            mY = y;
592        }
593        int mMoveGeneration;
594        int mFrame;
595        int mX;
596        int mY;
597    }
598
599    static class JSInterfaceData {
600        Object mObject;
601        String mInterfaceName;
602    }
603
604    static class JSKeyData {
605        String mCurrentText;
606        KeyEvent mEvent;
607    }
608
609    static class PostUrlData {
610        String mUrl;
611        byte[] mPostData;
612    }
613
614    static class DeleteSelectionData {
615        int mStart;
616        int mEnd;
617        int mTextGeneration;
618    }
619
620    static class ReplaceTextData {
621        String mReplace;
622        int mNewStart;
623        int mNewEnd;
624        int mTextGeneration;
625    }
626
627    static class TouchUpData {
628        int mMoveGeneration;
629        int mFrame;
630        int mNode;
631        int mX;
632        int mY;
633        int mSize;
634    }
635
636    static class TouchEventData {
637        int mAction;    // MotionEvent.getAction()
638        int mX;
639        int mY;
640    }
641
642    static class PluginStateData {
643        int mFrame;
644        int mNode;
645        int mState;
646    }
647
648    static class GeolocationPermissionsData {
649        String mOrigin;
650        boolean mAllow;
651        boolean mRemember;
652    }
653
654        static final String[] HandlerDebugString = {
655            "UPDATE_FRAME_CACHE_IF_LOADING", // = 98
656            "SCROLL_TEXT_INPUT", // = 99
657            "LOAD_URL", // = 100;
658            "STOP_LOADING", // = 101;
659            "RELOAD", // = 102;
660            "KEY_DOWN", // = 103;
661            "KEY_UP", // = 104;
662            "VIEW_SIZE_CHANGED", // = 105;
663            "GO_BACK_FORWARD", // = 106;
664            "SET_SCROLL_OFFSET", // = 107;
665            "RESTORE_STATE", // = 108;
666            "PAUSE_TIMERS", // = 109;
667            "RESUME_TIMERS", // = 110;
668            "CLEAR_CACHE", // = 111;
669            "CLEAR_HISTORY", // = 112;
670            "SET_SELECTION", // = 113;
671            "REPLACE_TEXT", // = 114;
672            "PASS_TO_JS", // = 115;
673            "SET_GLOBAL_BOUNDS", // = 116;
674            "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117;
675            "CLICK", // = 118;
676            "SET_NETWORK_STATE", // = 119;
677            "DOC_HAS_IMAGES", // = 120;
678            "121", // = 121;
679            "DELETE_SELECTION", // = 122;
680            "LISTBOX_CHOICES", // = 123;
681            "SINGLE_LISTBOX_CHOICE", // = 124;
682            "MESSAGE_RELAY", // = 125;
683            "SET_BACKGROUND_COLOR", // = 126;
684            "PLUGIN_STATE", // = 127;
685            "SAVE_DOCUMENT_STATE", // = 128;
686            "GET_SELECTION", // = 129;
687            "WEBKIT_DRAW", // = 130;
688            "SYNC_SCROLL", // = 131;
689            "POST_URL", // = 132;
690            "SPLIT_PICTURE_SET", // = 133;
691            "CLEAR_CONTENT", // = 134;
692            "SET_MOVE_MOUSE", // = 135;
693            "SET_MOVE_MOUSE_IF_LATEST", // = 136;
694            "REQUEST_CURSOR_HREF", // = 137;
695            "ADD_JS_INTERFACE", // = 138;
696            "LOAD_DATA", // = 139;
697            "TOUCH_UP", // = 140;
698            "TOUCH_EVENT", // = 141;
699            "SET_ACTIVE", // = 142;
700            "ON_PAUSE",     // = 143
701            "ON_RESUME",    // = 144
702            "FREE_MEMORY",  // = 145
703        };
704
705    class EventHub {
706        // Message Ids
707        static final int UPDATE_FRAME_CACHE_IF_LOADING = 98;
708        static final int SCROLL_TEXT_INPUT = 99;
709        static final int LOAD_URL = 100;
710        static final int STOP_LOADING = 101;
711        static final int RELOAD = 102;
712        static final int KEY_DOWN = 103;
713        static final int KEY_UP = 104;
714        static final int VIEW_SIZE_CHANGED = 105;
715        static final int GO_BACK_FORWARD = 106;
716        static final int SET_SCROLL_OFFSET = 107;
717        static final int RESTORE_STATE = 108;
718        static final int PAUSE_TIMERS = 109;
719        static final int RESUME_TIMERS = 110;
720        static final int CLEAR_CACHE = 111;
721        static final int CLEAR_HISTORY = 112;
722        static final int SET_SELECTION = 113;
723        static final int REPLACE_TEXT = 114;
724        static final int PASS_TO_JS = 115;
725        static final int SET_GLOBAL_BOUNDS = 116;
726        static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117;
727        static final int CLICK = 118;
728        static final int SET_NETWORK_STATE = 119;
729        static final int DOC_HAS_IMAGES = 120;
730        static final int DELETE_SELECTION = 122;
731        static final int LISTBOX_CHOICES = 123;
732        static final int SINGLE_LISTBOX_CHOICE = 124;
733        static final int MESSAGE_RELAY = 125;
734        static final int SET_BACKGROUND_COLOR = 126;
735        static final int PLUGIN_STATE = 127; // plugin notifications
736        static final int SAVE_DOCUMENT_STATE = 128;
737        static final int GET_SELECTION = 129;
738        static final int WEBKIT_DRAW = 130;
739        static final int SYNC_SCROLL = 131;
740        static final int POST_URL = 132;
741        static final int SPLIT_PICTURE_SET = 133;
742        static final int CLEAR_CONTENT = 134;
743
744        // UI nav messages
745        static final int SET_MOVE_MOUSE = 135;
746        static final int SET_MOVE_MOUSE_IF_LATEST = 136;
747        static final int REQUEST_CURSOR_HREF = 137;
748        static final int ADD_JS_INTERFACE = 138;
749        static final int LOAD_DATA = 139;
750
751        // motion
752        static final int TOUCH_UP = 140;
753        // message used to pass UI touch events to WebCore
754        static final int TOUCH_EVENT = 141;
755
756        // Used to tell the focus controller not to draw the blinking cursor,
757        // based on whether the WebView has focus and whether the WebView's
758        // cursor matches the webpage's focus.
759        static final int SET_ACTIVE = 142;
760
761        // lifecycle activities for just this DOM (unlike pauseTimers, which
762        // is global)
763        static final int ON_PAUSE = 143;
764        static final int ON_RESUME = 144;
765        static final int FREE_MEMORY = 145;
766
767        // Network-based messaging
768        static final int CLEAR_SSL_PREF_TABLE = 150;
769
770        // Test harness messages
771        static final int REQUEST_EXT_REPRESENTATION = 160;
772        static final int REQUEST_DOC_AS_TEXT = 161;
773
774        // debugging
775        static final int DUMP_DOMTREE = 170;
776        static final int DUMP_RENDERTREE = 171;
777        static final int DUMP_NAVTREE = 172;
778
779        static final int SET_JS_FLAGS = 173;
780        // Geolocation
781        static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180;
782
783        // private message ids
784        private static final int DESTROY =     200;
785
786        // Private handler for WebCore messages.
787        private Handler mHandler;
788        // Message queue for containing messages before the WebCore thread is
789        // ready.
790        private ArrayList<Message> mMessages = new ArrayList<Message>();
791        // Flag for blocking messages. This is used during DESTROY to avoid
792        // posting more messages to the EventHub or to WebView's event handler.
793        private boolean mBlockMessages;
794
795        private int mTid;
796        private int mSavedPriority;
797
798        /**
799         * Prevent other classes from creating an EventHub.
800         */
801        private EventHub() {}
802
803        /**
804         * Transfer all messages to the newly created webcore thread handler.
805         */
806        private void transferMessages() {
807            mTid = Process.myTid();
808            mSavedPriority = Process.getThreadPriority(mTid);
809
810            mHandler = new Handler() {
811                @Override
812                public void handleMessage(Message msg) {
813                    if (DebugFlags.WEB_VIEW_CORE) {
814                        Log.v(LOGTAG, (msg.what < UPDATE_FRAME_CACHE_IF_LOADING
815                                || msg.what
816                                > FREE_MEMORY ? Integer.toString(msg.what)
817                                : HandlerDebugString[msg.what
818                                        - UPDATE_FRAME_CACHE_IF_LOADING])
819                                + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
820                                + " obj=" + msg.obj);
821                    }
822                    switch (msg.what) {
823                        case WEBKIT_DRAW:
824                            webkitDraw();
825                            break;
826
827                        case DESTROY:
828                            // Time to take down the world. Cancel all pending
829                            // loads and destroy the native view and frame.
830                            mBrowserFrame.destroy();
831                            mBrowserFrame = null;
832                            mNativeClass = 0;
833                            break;
834
835                        case UPDATE_FRAME_CACHE_IF_LOADING:
836                            nativeUpdateFrameCacheIfLoading();
837                            break;
838
839                        case SCROLL_TEXT_INPUT:
840                            nativeScrollFocusedTextInput(msg.arg1, msg.arg2);
841                            break;
842
843                        case LOAD_URL:
844                            loadUrl((String) msg.obj);
845                            break;
846
847                        case POST_URL: {
848                            PostUrlData param = (PostUrlData) msg.obj;
849                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
850                            break;
851                        }
852                        case LOAD_DATA:
853                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
854                            String baseUrl = loadParams.mBaseUrl;
855                            if (baseUrl != null) {
856                                int i = baseUrl.indexOf(':');
857                                if (i > 0) {
858                                    /*
859                                     * In 1.0, {@link
860                                     * WebView#loadDataWithBaseURL} can access
861                                     * local asset files as long as the data is
862                                     * valid. In the new WebKit, the restriction
863                                     * is tightened. To be compatible with 1.0,
864                                     * we automatically add the scheme of the
865                                     * baseUrl for local access as long as it is
866                                     * not http(s)/ftp(s)/about/javascript
867                                     */
868                                    String scheme = baseUrl.substring(0, i);
869                                    if (!scheme.startsWith("http") &&
870                                            !scheme.startsWith("ftp") &&
871                                            !scheme.startsWith("about") &&
872                                            !scheme.startsWith("javascript")) {
873                                        nativeRegisterURLSchemeAsLocal(scheme);
874                                    }
875                                }
876                            }
877                            mBrowserFrame.loadData(baseUrl,
878                                    loadParams.mData,
879                                    loadParams.mMimeType,
880                                    loadParams.mEncoding,
881                                    loadParams.mFailUrl);
882                            break;
883
884                        case STOP_LOADING:
885                            // If the WebCore has committed the load, but not
886                            // finished the first layout yet, we need to set
887                            // first layout done to trigger the interpreted side sync
888                            // up with native side
889                            if (mBrowserFrame.committed()
890                                    && !mBrowserFrame.firstLayoutDone()) {
891                                mBrowserFrame.didFirstLayout();
892                            }
893                            // Do this after syncing up the layout state.
894                            stopLoading();
895                            break;
896
897                        case RELOAD:
898                            mBrowserFrame.reload(false);
899                            break;
900
901                        case KEY_DOWN:
902                            key((KeyEvent) msg.obj, true);
903                            break;
904
905                        case KEY_UP:
906                            key((KeyEvent) msg.obj, false);
907                            break;
908
909                        case CLICK:
910                            nativeClick(msg.arg1, msg.arg2);
911                            break;
912
913                        case VIEW_SIZE_CHANGED: {
914                            WebView.ViewSizeData data =
915                                    (WebView.ViewSizeData) msg.obj;
916                            viewSizeChanged(data.mWidth, data.mHeight,
917                                    data.mTextWrapWidth, data.mScale);
918                            break;
919                        }
920                        case SET_SCROLL_OFFSET:
921                            // note: these are in document coordinates
922                            // (inv-zoom)
923                            Point pt = (Point) msg.obj;
924                            nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
925                            break;
926
927                        case SET_GLOBAL_BOUNDS:
928                            Rect r = (Rect) msg.obj;
929                            nativeSetGlobalBounds(r.left, r.top, r.width(),
930                                r.height());
931                            break;
932
933                        case GO_BACK_FORWARD:
934                            // If it is a standard load and the load is not
935                            // committed yet, we interpret BACK as RELOAD
936                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
937                                    (mBrowserFrame.loadType() ==
938                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
939                                mBrowserFrame.reload(true);
940                            } else {
941                                mBrowserFrame.goBackOrForward(msg.arg1);
942                            }
943                            break;
944
945                        case RESTORE_STATE:
946                            stopLoading();
947                            restoreState(msg.arg1);
948                            break;
949
950                        case PAUSE_TIMERS:
951                            mSavedPriority = Process.getThreadPriority(mTid);
952                            Process.setThreadPriority(mTid,
953                                    Process.THREAD_PRIORITY_BACKGROUND);
954                            pauseTimers();
955                            if (CacheManager.disableTransaction()) {
956                                WebCoreThread.mCacheTickersBlocked = true;
957                                sWebCoreHandler.removeMessages(
958                                        WebCoreThread.CACHE_TICKER);
959                            }
960                            break;
961
962                        case RESUME_TIMERS:
963                            Process.setThreadPriority(mTid, mSavedPriority);
964                            resumeTimers();
965                            if (CacheManager.enableTransaction()) {
966                                WebCoreThread.mCacheTickersBlocked = false;
967                                sWebCoreHandler.sendMessageDelayed(
968                                        sWebCoreHandler.obtainMessage(
969                                        WebCoreThread.CACHE_TICKER),
970                                        WebCoreThread.CACHE_TICKER_INTERVAL);
971                            }
972                            break;
973
974                        case ON_PAUSE:
975                            nativePause();
976                            break;
977
978                        case ON_RESUME:
979                            nativeResume();
980                            break;
981
982                        case FREE_MEMORY:
983                            clearCache(false);
984                            nativeFreeMemory();
985                            break;
986
987                        case PLUGIN_STATE:
988                            PluginStateData psd = (PluginStateData) msg.obj;
989                            nativeUpdatePluginState(psd.mFrame, psd.mNode, psd.mState);
990                            break;
991
992                        case SET_NETWORK_STATE:
993                            if (BrowserFrame.sJavaBridge == null) {
994                                throw new IllegalStateException("No WebView " +
995                                        "has been created in this process!");
996                            }
997                            BrowserFrame.sJavaBridge
998                                    .setNetworkOnLine(msg.arg1 == 1);
999                            break;
1000
1001                        case CLEAR_CACHE:
1002                            clearCache(msg.arg1 == 1);
1003                            break;
1004
1005                        case CLEAR_HISTORY:
1006                            mCallbackProxy.getBackForwardList().
1007                                    close(mBrowserFrame.mNativeFrame);
1008                            break;
1009
1010                        case REPLACE_TEXT:
1011                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
1012                            nativeReplaceTextfieldText(msg.arg1, msg.arg2,
1013                                    rep.mReplace, rep.mNewStart, rep.mNewEnd,
1014                                    rep.mTextGeneration);
1015                            break;
1016
1017                        case PASS_TO_JS: {
1018                            JSKeyData jsData = (JSKeyData) msg.obj;
1019                            KeyEvent evt = jsData.mEvent;
1020                            int keyCode = evt.getKeyCode();
1021                            int keyValue = evt.getUnicodeChar();
1022                            int generation = msg.arg1;
1023                            passToJs(generation,
1024                                    jsData.mCurrentText,
1025                                    keyCode,
1026                                    keyValue,
1027                                    evt.isDown(),
1028                                    evt.isShiftPressed(), evt.isAltPressed(),
1029                                    evt.isSymPressed());
1030                            break;
1031                        }
1032
1033                        case SAVE_DOCUMENT_STATE: {
1034                            CursorData cDat = (CursorData) msg.obj;
1035                            nativeSaveDocumentState(cDat.mFrame);
1036                            break;
1037                        }
1038
1039                        case CLEAR_SSL_PREF_TABLE:
1040                            Network.getInstance(mContext)
1041                                    .clearUserSslPrefTable();
1042                            break;
1043
1044                        case TOUCH_UP:
1045                            TouchUpData touchUpData = (TouchUpData) msg.obj;
1046                            nativeTouchUp(touchUpData.mMoveGeneration,
1047                                    touchUpData.mFrame, touchUpData.mNode,
1048                                    touchUpData.mX, touchUpData.mY,
1049                                    touchUpData.mSize);
1050                            break;
1051
1052                        case TOUCH_EVENT: {
1053                            TouchEventData ted = (TouchEventData) msg.obj;
1054                            Message.obtain(
1055                                    mWebView.mPrivateHandler,
1056                                    WebView.PREVENT_TOUCH_ID, ted.mAction,
1057                                    nativeHandleTouchEvent(ted.mAction, ted.mX,
1058                                            ted.mY) ? 1 : 0).sendToTarget();
1059                            break;
1060                        }
1061
1062                        case SET_ACTIVE:
1063                            nativeSetFocusControllerActive(msg.arg1 == 1);
1064                            break;
1065
1066                        case ADD_JS_INTERFACE:
1067                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
1068                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
1069                                    jsData.mInterfaceName);
1070                            break;
1071
1072                        case REQUEST_EXT_REPRESENTATION:
1073                            mBrowserFrame.externalRepresentation(
1074                                    (Message) msg.obj);
1075                            break;
1076
1077                        case REQUEST_DOC_AS_TEXT:
1078                            mBrowserFrame.documentAsText((Message) msg.obj);
1079                            break;
1080
1081                        case SET_MOVE_MOUSE:
1082                            CursorData cursorData = (CursorData) msg.obj;
1083                            nativeMoveMouse(cursorData.mFrame,
1084                                     cursorData.mX, cursorData.mY);
1085                            break;
1086
1087                        case SET_MOVE_MOUSE_IF_LATEST:
1088                            CursorData cData = (CursorData) msg.obj;
1089                            nativeMoveMouseIfLatest(cData.mMoveGeneration,
1090                                    cData.mFrame,
1091                                    cData.mX, cData.mY);
1092                            break;
1093
1094                        case REQUEST_CURSOR_HREF: {
1095                            Message hrefMsg = (Message) msg.obj;
1096                            String res = nativeRetrieveHref(msg.arg1, msg.arg2);
1097                            hrefMsg.getData().putString("url", res);
1098                            hrefMsg.sendToTarget();
1099                            break;
1100                        }
1101
1102                        case UPDATE_CACHE_AND_TEXT_ENTRY:
1103                            nativeUpdateFrameCache();
1104                            // FIXME: this should provide a minimal rectangle
1105                            if (mWebView != null) {
1106                                mWebView.postInvalidate();
1107                            }
1108                            sendUpdateTextEntry();
1109                            break;
1110
1111                        case DOC_HAS_IMAGES:
1112                            Message imageResult = (Message) msg.obj;
1113                            imageResult.arg1 =
1114                                    mBrowserFrame.documentHasImages() ? 1 : 0;
1115                            imageResult.sendToTarget();
1116                            break;
1117
1118                        case DELETE_SELECTION:
1119                            DeleteSelectionData deleteSelectionData
1120                                    = (DeleteSelectionData) msg.obj;
1121                            nativeDeleteSelection(deleteSelectionData.mStart,
1122                                    deleteSelectionData.mEnd,
1123                                    deleteSelectionData.mTextGeneration);
1124                            break;
1125
1126                        case SET_SELECTION:
1127                            nativeSetSelection(msg.arg1, msg.arg2);
1128                            break;
1129
1130                        case LISTBOX_CHOICES:
1131                            SparseBooleanArray choices = (SparseBooleanArray)
1132                                    msg.obj;
1133                            int choicesSize = msg.arg1;
1134                            boolean[] choicesArray = new boolean[choicesSize];
1135                            for (int c = 0; c < choicesSize; c++) {
1136                                choicesArray[c] = choices.get(c);
1137                            }
1138                            nativeSendListBoxChoices(choicesArray,
1139                                    choicesSize);
1140                            break;
1141
1142                        case SINGLE_LISTBOX_CHOICE:
1143                            nativeSendListBoxChoice(msg.arg1);
1144                            break;
1145
1146                        case SET_BACKGROUND_COLOR:
1147                            nativeSetBackgroundColor(msg.arg1);
1148                            break;
1149
1150                        case GET_SELECTION:
1151                            String str = nativeGetSelection((Region) msg.obj);
1152                            Message.obtain(mWebView.mPrivateHandler
1153                                    , WebView.UPDATE_CLIPBOARD, str)
1154                                    .sendToTarget();
1155                            break;
1156
1157                        case DUMP_DOMTREE:
1158                            nativeDumpDomTree(msg.arg1 == 1);
1159                            break;
1160
1161                        case DUMP_RENDERTREE:
1162                            nativeDumpRenderTree(msg.arg1 == 1);
1163                            break;
1164
1165                        case DUMP_NAVTREE:
1166                            nativeDumpNavTree();
1167                            break;
1168
1169                        case SET_JS_FLAGS:
1170                            nativeSetJsFlags((String)msg.obj);
1171                            break;
1172
1173                        case GEOLOCATION_PERMISSIONS_PROVIDE:
1174                            GeolocationPermissionsData data =
1175                                    (GeolocationPermissionsData) msg.obj;
1176                            nativeGeolocationPermissionsProvide(data.mOrigin,
1177                                    data.mAllow, data.mRemember);
1178                            break;
1179
1180                        case SYNC_SCROLL:
1181                            mWebkitScrollX = msg.arg1;
1182                            mWebkitScrollY = msg.arg2;
1183                            break;
1184
1185                        case SPLIT_PICTURE_SET:
1186                            nativeSplitContent();
1187                            mSplitPictureIsScheduled = false;
1188                            break;
1189
1190                        case CLEAR_CONTENT:
1191                            // Clear the view so that onDraw() will draw nothing
1192                            // but white background
1193                            // (See public method WebView.clearView)
1194                            nativeClearContent();
1195                            break;
1196
1197                        case MESSAGE_RELAY:
1198                            if (msg.obj instanceof Message) {
1199                                ((Message) msg.obj).sendToTarget();
1200                            }
1201                            break;
1202                    }
1203                }
1204            };
1205            // Take all queued messages and resend them to the new handler.
1206            synchronized (this) {
1207                int size = mMessages.size();
1208                for (int i = 0; i < size; i++) {
1209                    mHandler.sendMessage(mMessages.get(i));
1210                }
1211                mMessages = null;
1212            }
1213        }
1214
1215        /**
1216         * Send a message internally to the queue or to the handler
1217         */
1218        private synchronized void sendMessage(Message msg) {
1219            if (mBlockMessages) {
1220                return;
1221            }
1222            if (mMessages != null) {
1223                mMessages.add(msg);
1224            } else {
1225                mHandler.sendMessage(msg);
1226            }
1227        }
1228
1229        private synchronized void removeMessages(int what) {
1230            if (mBlockMessages) {
1231                return;
1232            }
1233            if (what == EventHub.WEBKIT_DRAW) {
1234                mDrawIsScheduled = false;
1235            }
1236            if (mMessages != null) {
1237                Log.w(LOGTAG, "Not supported in this case.");
1238            } else {
1239                mHandler.removeMessages(what);
1240            }
1241        }
1242
1243        private synchronized void sendMessageDelayed(Message msg, long delay) {
1244            if (mBlockMessages) {
1245                return;
1246            }
1247            mHandler.sendMessageDelayed(msg, delay);
1248        }
1249
1250        /**
1251         * Send a message internally to the front of the queue.
1252         */
1253        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1254            if (mBlockMessages) {
1255                return;
1256            }
1257            if (mMessages != null) {
1258                mMessages.add(0, msg);
1259            } else {
1260                mHandler.sendMessageAtFrontOfQueue(msg);
1261            }
1262        }
1263
1264        /**
1265         * Remove all the messages.
1266         */
1267        private synchronized void removeMessages() {
1268            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1269            mDrawIsScheduled = false;
1270            mSplitPictureIsScheduled = false;
1271            if (mMessages != null) {
1272                mMessages.clear();
1273            } else {
1274                mHandler.removeCallbacksAndMessages(null);
1275            }
1276        }
1277
1278        /**
1279         * Block sending messages to the EventHub.
1280         */
1281        private synchronized void blockMessages() {
1282            mBlockMessages = true;
1283        }
1284    }
1285
1286    //-------------------------------------------------------------------------
1287    // Methods called by host activity (in the same thread)
1288    //-------------------------------------------------------------------------
1289
1290    void stopLoading() {
1291        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1292        if (mBrowserFrame != null) {
1293            mBrowserFrame.stopLoading();
1294        }
1295    }
1296
1297    //-------------------------------------------------------------------------
1298    // Methods called by WebView
1299    // If it refers to local variable, it needs synchronized().
1300    // If it needs WebCore, it has to send message.
1301    //-------------------------------------------------------------------------
1302
1303    void sendMessage(Message msg) {
1304        mEventHub.sendMessage(msg);
1305    }
1306
1307    void sendMessage(int what) {
1308        mEventHub.sendMessage(Message.obtain(null, what));
1309    }
1310
1311    void sendMessage(int what, Object obj) {
1312        mEventHub.sendMessage(Message.obtain(null, what, obj));
1313    }
1314
1315    void sendMessage(int what, int arg1) {
1316        // just ignore the second argument (make it 0)
1317        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1318    }
1319
1320    void sendMessage(int what, int arg1, int arg2) {
1321        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1322    }
1323
1324    void sendMessage(int what, int arg1, Object obj) {
1325        // just ignore the second argument (make it 0)
1326        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1327    }
1328
1329    void sendMessage(int what, int arg1, int arg2, Object obj) {
1330        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1331    }
1332
1333    void sendMessageDelayed(int what, Object obj, long delay) {
1334        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1335    }
1336
1337    void removeMessages(int what) {
1338        mEventHub.removeMessages(what);
1339    }
1340
1341    void removeMessages() {
1342        mEventHub.removeMessages();
1343    }
1344
1345    /**
1346     * Removes pending messages and trigger a DESTROY message to send to
1347     * WebCore.
1348     * Called from UI thread.
1349     */
1350    void destroy() {
1351        // We don't want anyone to post a message between removing pending
1352        // messages and sending the destroy message.
1353        synchronized (mEventHub) {
1354            mEventHub.removeMessages();
1355            mEventHub.sendMessageAtFrontOfQueue(
1356                    Message.obtain(null, EventHub.DESTROY));
1357            mEventHub.blockMessages();
1358            mWebView = null;
1359        }
1360    }
1361
1362    //-------------------------------------------------------------------------
1363    // WebViewCore private methods
1364    //-------------------------------------------------------------------------
1365
1366    private void clearCache(boolean includeDiskFiles) {
1367        mBrowserFrame.clearCache();
1368        if (includeDiskFiles) {
1369            CacheManager.removeAllCacheFiles();
1370        }
1371    }
1372
1373    private void loadUrl(String url) {
1374        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1375        mBrowserFrame.loadUrl(url);
1376    }
1377
1378    private void key(KeyEvent evt, boolean isDown) {
1379        if (DebugFlags.WEB_VIEW_CORE) {
1380            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1381                    + evt);
1382        }
1383        int keyCode = evt.getKeyCode();
1384        if (!nativeKey(keyCode, evt.getUnicodeChar(),
1385                evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
1386                evt.isSymPressed(),
1387                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1388            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1389                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1390                if (DebugFlags.WEB_VIEW_CORE) {
1391                    Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
1392                }
1393                if (mWebView != null && evt.isDown()) {
1394                    Message.obtain(mWebView.mPrivateHandler,
1395                            WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
1396                }
1397                return;
1398            }
1399            // bubble up the event handling
1400            // but do not bubble up the ENTER key, which would open the search
1401            // bar without any text.
1402            mCallbackProxy.onUnhandledKeyEvent(evt);
1403        }
1404    }
1405
1406    // These values are used to avoid requesting a layout based on old values
1407    private int mCurrentViewWidth = 0;
1408    private int mCurrentViewHeight = 0;
1409    private float mCurrentViewScale = 1.0f;
1410
1411    // notify webkit that our virtual view size changed size (after inv-zoom)
1412    private void viewSizeChanged(int w, int h, int textwrapWidth, float scale) {
1413        if (DebugFlags.WEB_VIEW_CORE) {
1414            Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
1415                    + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
1416        }
1417        if (w == 0) {
1418            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1419            return;
1420        }
1421        int width = w;
1422        if (mSettings.getUseWideViewPort()) {
1423            if (mViewportWidth == -1) {
1424                if (mSettings.getLayoutAlgorithm() ==
1425                        WebSettings.LayoutAlgorithm.NORMAL) {
1426                    width = DEFAULT_VIEWPORT_WIDTH;
1427                } else {
1428                    /*
1429                     * if a page's minimum preferred width is wider than the
1430                     * given "w", use it instead to get better layout result. If
1431                     * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1432                     * wider. If we start a page with screen width, due to the
1433                     * delay between {@link #didFirstLayout} and
1434                     * {@link #viewSizeChanged},
1435                     * {@link #nativeGetContentMinPrefWidth} will return a more
1436                     * accurate value than initial 0 to result a better layout.
1437                     * In the worse case, the native width will be adjusted when
1438                     * next zoom or screen orientation change happens.
1439                     */
1440                    width = Math.max(w, Math.max(DEFAULT_VIEWPORT_WIDTH,
1441                            nativeGetContentMinPrefWidth()));
1442                }
1443            } else {
1444                width = Math.max(w, mViewportWidth);
1445            }
1446        }
1447        nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
1448                textwrapWidth, scale, w, h);
1449        // Remember the current width and height
1450        boolean needInvalidate = (mCurrentViewWidth == 0);
1451        mCurrentViewWidth = w;
1452        mCurrentViewHeight = h;
1453        mCurrentViewScale = scale;
1454        if (needInvalidate) {
1455            // ensure {@link #webkitDraw} is called as we were blocking in
1456            // {@link #contentDraw} when mCurrentViewWidth is 0
1457            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1458            contentDraw();
1459        }
1460        mEventHub.sendMessage(Message.obtain(null,
1461                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1462    }
1463
1464    private void sendUpdateTextEntry() {
1465        if (mWebView != null) {
1466            Message.obtain(mWebView.mPrivateHandler,
1467                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1468        }
1469    }
1470
1471    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1472    // callbacks. Computes the sum of database quota for all origins.
1473    private long getUsedQuota() {
1474        WebStorage webStorage = WebStorage.getInstance();
1475        Set<String> origins = webStorage.getOrigins();
1476        if (origins == null) {
1477            return 0;
1478        }
1479        long usedQuota = 0;
1480        for (String origin : origins) {
1481            usedQuota += webStorage.getQuotaForOrigin(origin);
1482        }
1483        return usedQuota;
1484    }
1485
1486    // Used to avoid posting more than one draw message.
1487    private boolean mDrawIsScheduled;
1488
1489    // Used to avoid posting more than one split picture message.
1490    private boolean mSplitPictureIsScheduled;
1491
1492    // Used to suspend drawing.
1493    private boolean mDrawIsPaused;
1494
1495    // Used to end scale+scroll mode, accessed by both threads
1496    boolean mEndScaleZoom = false;
1497
1498    // mRestoreState is set in didFirstLayout(), and reset in the next
1499    // webkitDraw after passing it to the UI thread.
1500    private RestoreState mRestoreState = null;
1501
1502    static class RestoreState {
1503        float mMinScale;
1504        float mMaxScale;
1505        float mViewScale;
1506        float mTextWrapScale;
1507        int mScrollX;
1508        int mScrollY;
1509    }
1510
1511    static class DrawData {
1512        DrawData() {
1513            mInvalRegion = new Region();
1514            mWidthHeight = new Point();
1515        }
1516        Region mInvalRegion;
1517        Point mViewPoint;
1518        Point mWidthHeight;
1519        int mMinPrefWidth;
1520        RestoreState mRestoreState; // only non-null if it is for the first
1521                                    // picture set after the first layout
1522    }
1523
1524    private void webkitDraw() {
1525        mDrawIsScheduled = false;
1526        DrawData draw = new DrawData();
1527        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
1528        if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
1529                == false) {
1530            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
1531            return;
1532        }
1533        if (mWebView != null) {
1534            // Send the native view size that was used during the most recent
1535            // layout.
1536            draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1537            if (WebView.ENABLE_DOUBLETAP_ZOOM && mSettings.getUseWideViewPort()) {
1538                draw.mMinPrefWidth = Math.max(DEFAULT_VIEWPORT_WIDTH,
1539                        nativeGetContentMinPrefWidth());
1540            }
1541            if (mRestoreState != null) {
1542                draw.mRestoreState = mRestoreState;
1543                mRestoreState = null;
1544            }
1545            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1546            Message.obtain(mWebView.mPrivateHandler,
1547                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
1548            if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1549                // as we have the new picture, try to sync the scroll position
1550                Message.obtain(mWebView.mPrivateHandler,
1551                        WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1552                        mWebkitScrollY).sendToTarget();
1553                mWebkitScrollX = mWebkitScrollY = 0;
1554            }
1555        }
1556    }
1557
1558    ///////////////////////////////////////////////////////////////////////////
1559    // These are called from the UI thread, not our thread
1560
1561    static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
1562                                         Paint.DITHER_FLAG |
1563                                         Paint.SUBPIXEL_TEXT_FLAG;
1564    static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
1565                                           Paint.DITHER_FLAG;
1566
1567    final DrawFilter mZoomFilter =
1568                    new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
1569    final DrawFilter mScrollFilter =
1570                    new PaintFlagsDrawFilter(SCROLL_BITS, 0);
1571
1572    /* package */ void drawContentPicture(Canvas canvas, int color,
1573                                          boolean animatingZoom,
1574                                          boolean animatingScroll) {
1575        DrawFilter df = null;
1576        if (animatingZoom) {
1577            df = mZoomFilter;
1578        } else if (animatingScroll) {
1579            df = mScrollFilter;
1580        }
1581        canvas.setDrawFilter(df);
1582        boolean tookTooLong = nativeDrawContent(canvas, color);
1583        canvas.setDrawFilter(null);
1584        if (tookTooLong && mSplitPictureIsScheduled == false) {
1585            mSplitPictureIsScheduled = true;
1586            sendMessage(EventHub.SPLIT_PICTURE_SET);
1587        }
1588    }
1589
1590    /* package */ boolean pictureReady() {
1591        return nativePictureReady();
1592    }
1593
1594    /*package*/ Picture copyContentPicture() {
1595        Picture result = new Picture();
1596        nativeCopyContentToPicture(result);
1597        return result;
1598    }
1599
1600    static void pauseUpdate(WebViewCore core) {
1601        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1602        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1603        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1604        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1605                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1606        // Note: there is one possible failure mode. If pauseUpdate() is called
1607        // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
1608        // of the queue and about to be executed. mDrawIsScheduled may be set to
1609        // false in webkitDraw(). So update won't be blocked. But at least the
1610        // webcore thread priority is still lowered.
1611        if (core != null) {
1612            synchronized (core) {
1613                core.mDrawIsPaused = true;
1614                core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1615            }
1616        }
1617    }
1618
1619    static void resumeUpdate(WebViewCore core) {
1620        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1621        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1622        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1623        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1624                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1625        if (core != null) {
1626            synchronized (core) {
1627                core.mDrawIsScheduled = false;
1628                core.mDrawIsPaused = false;
1629                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate");
1630                core.contentDraw();
1631            }
1632        }
1633    }
1634
1635    static void startCacheTransaction() {
1636        sWebCoreHandler.sendMessage(sWebCoreHandler
1637                .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
1638    }
1639
1640    static void endCacheTransaction() {
1641        sWebCoreHandler.sendMessage(sWebCoreHandler
1642                .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
1643    }
1644
1645    //////////////////////////////////////////////////////////////////////////
1646
1647    private void restoreState(int index) {
1648        WebBackForwardList list = mCallbackProxy.getBackForwardList();
1649        int size = list.getSize();
1650        for (int i = 0; i < size; i++) {
1651            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1652        }
1653        mBrowserFrame.mLoadInitFromJava = true;
1654        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1655        mBrowserFrame.mLoadInitFromJava = false;
1656    }
1657
1658    //-------------------------------------------------------------------------
1659    // Implement abstract methods in WebViewCore, native WebKit callback part
1660    //-------------------------------------------------------------------------
1661
1662    // called from JNI or WebView thread
1663    /* package */ void contentDraw() {
1664        // don't update the Picture until we have an initial width and finish
1665        // the first layout
1666        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1667            return;
1668        }
1669        // only fire an event if this is our first request
1670        synchronized (this) {
1671            if (mDrawIsPaused || mDrawIsScheduled) {
1672                return;
1673            }
1674            mDrawIsScheduled = true;
1675            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1676        }
1677    }
1678
1679    // called by JNI
1680    private void contentScrollBy(int dx, int dy, boolean animate) {
1681        if (!mBrowserFrame.firstLayoutDone()) {
1682            // Will this happen? If yes, we need to do something here.
1683            return;
1684        }
1685        if (mWebView != null) {
1686            Message msg = Message.obtain(mWebView.mPrivateHandler,
1687                    WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
1688            if (mDrawIsScheduled) {
1689                mEventHub.sendMessage(Message.obtain(null,
1690                        EventHub.MESSAGE_RELAY, msg));
1691            } else {
1692                msg.sendToTarget();
1693            }
1694        }
1695    }
1696
1697    // called by JNI
1698    private void contentScrollTo(int x, int y) {
1699        if (!mBrowserFrame.firstLayoutDone()) {
1700            /*
1701             * WebKit restore state will be called before didFirstLayout(),
1702             * remember the position as it has to be applied after restoring
1703             * zoom factor which is controlled by screenWidth.
1704             */
1705            mRestoredX = x;
1706            mRestoredY = y;
1707            return;
1708        }
1709        if (mWebView != null) {
1710            Message msg = Message.obtain(mWebView.mPrivateHandler,
1711                    WebView.SCROLL_TO_MSG_ID, x, y);
1712            if (mDrawIsScheduled) {
1713                mEventHub.sendMessage(Message.obtain(null,
1714                        EventHub.MESSAGE_RELAY, msg));
1715            } else {
1716                msg.sendToTarget();
1717            }
1718        }
1719    }
1720
1721    // called by JNI
1722    private void contentSpawnScrollTo(int x, int y) {
1723        if (!mBrowserFrame.firstLayoutDone()) {
1724            /*
1725             * WebKit restore state will be called before didFirstLayout(),
1726             * remember the position as it has to be applied after restoring
1727             * zoom factor which is controlled by screenWidth.
1728             */
1729            mRestoredX = x;
1730            mRestoredY = y;
1731            return;
1732        }
1733        if (mWebView != null) {
1734            Message msg = Message.obtain(mWebView.mPrivateHandler,
1735                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
1736            if (mDrawIsScheduled) {
1737                mEventHub.sendMessage(Message.obtain(null,
1738                        EventHub.MESSAGE_RELAY, msg));
1739            } else {
1740                msg.sendToTarget();
1741            }
1742        }
1743    }
1744
1745    // called by JNI
1746    private void sendNotifyProgressFinished() {
1747        sendUpdateTextEntry();
1748        // as CacheManager can behave based on database transaction, we need to
1749        // call tick() to trigger endTransaction
1750        sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
1751        sWebCoreHandler.sendMessage(sWebCoreHandler
1752                .obtainMessage(WebCoreThread.CACHE_TICKER));
1753        contentDraw();
1754    }
1755
1756    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
1757        be scaled before they can be used by the view system, which happens
1758        in WebView since it (and its thread) know the current scale factor.
1759     */
1760    private void sendViewInvalidate(int left, int top, int right, int bottom) {
1761        if (mWebView != null) {
1762            Message.obtain(mWebView.mPrivateHandler,
1763                           WebView.INVAL_RECT_MSG_ID,
1764                           new Rect(left, top, right, bottom)).sendToTarget();
1765        }
1766    }
1767
1768    /* package */ WebView getWebView() {
1769        return mWebView;
1770    }
1771
1772    private native void setViewportSettingsFromNative();
1773
1774    // called by JNI
1775    private void didFirstLayout(boolean standardLoad) {
1776        if (DebugFlags.WEB_VIEW_CORE) {
1777            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
1778        }
1779
1780        mBrowserFrame.didFirstLayout();
1781
1782        // reset the scroll position as it is a new page now
1783        mWebkitScrollX = mWebkitScrollY = 0;
1784
1785        // for non-standard load, we only adjust scale if mRestoredScale > 0
1786        if (mWebView == null || (mRestoredScale == 0 && !standardLoad)) return;
1787
1788        // set the viewport settings from WebKit
1789        setViewportSettingsFromNative();
1790
1791        // adjust the default scale to match the density
1792        if (WebView.DEFAULT_SCALE_PERCENT != 100) {
1793            float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f;
1794            if (mViewportInitialScale > 0) {
1795                mViewportInitialScale *= adjust;
1796            }
1797            if (mViewportMinimumScale > 0) {
1798                mViewportMinimumScale *= adjust;
1799            }
1800            if (mViewportMaximumScale > 0) {
1801                mViewportMaximumScale *= adjust;
1802            }
1803        }
1804
1805        // infer the values if they are not defined.
1806        if (mViewportWidth == 0) {
1807            if (mViewportInitialScale == 0) {
1808                mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT;
1809            }
1810            if (mViewportMinimumScale == 0) {
1811                mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT;
1812            }
1813        }
1814        if (mViewportUserScalable == false) {
1815            mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT;
1816            mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT;
1817            mViewportMaximumScale = WebView.DEFAULT_SCALE_PERCENT;
1818        }
1819        if (mViewportMinimumScale > mViewportInitialScale) {
1820            if (mViewportInitialScale == 0) {
1821                mViewportInitialScale = mViewportMinimumScale;
1822            } else {
1823                mViewportMinimumScale = mViewportInitialScale;
1824            }
1825        }
1826        if (mViewportMaximumScale > 0) {
1827            if (mViewportMaximumScale < mViewportInitialScale) {
1828                mViewportMaximumScale = mViewportInitialScale;
1829            } else if (mViewportInitialScale == 0) {
1830                mViewportInitialScale = mViewportMaximumScale;
1831            }
1832        }
1833        if (mViewportWidth < 0
1834                && mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) {
1835            mViewportWidth = 0;
1836        }
1837
1838        // now notify webview
1839        int webViewWidth = Math.round(mCurrentViewWidth * mCurrentViewScale);
1840        mRestoreState = new RestoreState();
1841        mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
1842        mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
1843        mRestoreState.mScrollX = mRestoredX;
1844        mRestoreState.mScrollY = mRestoredY;
1845        if (mRestoredScale > 0) {
1846            if (mRestoredScreenWidthScale > 0) {
1847                mRestoreState.mTextWrapScale =
1848                        mRestoredScreenWidthScale / 100.0f;
1849                // 0 will trigger WebView to turn on zoom overview mode
1850                mRestoreState.mViewScale = 0;
1851            } else {
1852                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1853                        mRestoredScale / 100.0f;
1854            }
1855        } else {
1856            if (mViewportInitialScale > 0) {
1857                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1858                        mViewportInitialScale / 100.0f;
1859            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
1860                mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1861                        (float) webViewWidth / mViewportWidth;
1862            } else {
1863                mRestoreState.mTextWrapScale =
1864                        WebView.DEFAULT_SCALE_PERCENT / 100.0f;
1865                // 0 will trigger WebView to turn on zoom overview mode
1866                mRestoreState.mViewScale = 0;
1867            }
1868        }
1869
1870        if (mWebView.mHeightCanMeasure) {
1871            // Trick to ensure that the Picture has the exact height for the
1872            // content by forcing to layout with 0 height after the page is
1873            // ready, which is indicated by didFirstLayout. This is essential to
1874            // get rid of the white space in the GMail which uses WebView for
1875            // message view.
1876            mWebView.mLastHeightSent = 0;
1877            // Send a negative scale to indicate that WebCore should reuse
1878            // the current scale
1879            WebView.ViewSizeData data = new WebView.ViewSizeData();
1880            data.mWidth = mWebView.mLastWidthSent;
1881            data.mHeight = 0;
1882            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
1883            // true. It is safe to use mWidth for mTextWrapWidth.
1884            data.mTextWrapWidth = data.mWidth;
1885            data.mScale = -1.0f;
1886            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
1887                    EventHub.VIEW_SIZE_CHANGED, data));
1888        } else if (mSettings.getUseWideViewPort() && mCurrentViewWidth > 0) {
1889            WebView.ViewSizeData data = new WebView.ViewSizeData();
1890            // mViewScale as 0 means it is in zoom overview mode. So we don't
1891            // know the exact scale. If mRestoredScale is non-zero, use it;
1892            // otherwise just use mTextWrapScale as the initial scale.
1893            data.mScale = mRestoreState.mViewScale == 0
1894                    ? (mRestoredScale > 0 ? mRestoredScale
1895                            : mRestoreState.mTextWrapScale)
1896                    : mRestoreState.mViewScale;
1897            data.mWidth = Math.round(webViewWidth / data.mScale);
1898            data.mHeight = mCurrentViewHeight * data.mWidth
1899                    / mCurrentViewWidth;
1900            data.mTextWrapWidth = Math.round(webViewWidth
1901                    / mRestoreState.mTextWrapScale);
1902            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
1903                    EventHub.VIEW_SIZE_CHANGED, data));
1904        }
1905        // reset restored offset, scale
1906        mRestoredX = mRestoredY = mRestoredScale = mRestoredScreenWidthScale = 0;
1907    }
1908
1909    // called by JNI
1910    private void restoreScale(int scale) {
1911        if (mBrowserFrame.firstLayoutDone() == false) {
1912            mRestoredScale = scale;
1913        }
1914    }
1915
1916    // called by JNI
1917    private void restoreScreenWidthScale(int scale) {
1918        if (!WebView.ENABLE_DOUBLETAP_ZOOM || !mSettings.getUseWideViewPort()) {
1919            return;
1920        }
1921
1922        if (mBrowserFrame.firstLayoutDone() == false) {
1923            mRestoredScreenWidthScale = scale;
1924        }
1925    }
1926
1927    // called by JNI
1928    private void needTouchEvents(boolean need) {
1929        if (mWebView != null) {
1930            Message.obtain(mWebView.mPrivateHandler,
1931                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
1932                    .sendToTarget();
1933        }
1934    }
1935
1936    // called by JNI
1937    private void updateTextfield(int ptr, boolean changeToPassword,
1938            String text, int textGeneration) {
1939        if (mWebView != null) {
1940            Message msg = Message.obtain(mWebView.mPrivateHandler,
1941                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
1942                    textGeneration, text);
1943            msg.getData().putBoolean("password", changeToPassword);
1944            msg.sendToTarget();
1945        }
1946    }
1947
1948    // called by JNI
1949    private void clearTextEntry() {
1950        if (mWebView == null) return;
1951        Message.obtain(mWebView.mPrivateHandler,
1952                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
1953    }
1954
1955    private native void nativeUpdateFrameCacheIfLoading();
1956
1957    /**
1958     * Scroll the focused textfield to (x, y) in document space
1959     */
1960    private native void nativeScrollFocusedTextInput(int x, int y);
1961
1962    // these must be in document space (i.e. not scaled/zoomed).
1963    private native void nativeSetScrollOffset(int gen, int dx, int dy);
1964
1965    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
1966
1967    // called by JNI
1968    private void requestListBox(String[] array, boolean[] enabledArray,
1969            int[] selectedArray) {
1970        if (mWebView != null) {
1971            mWebView.requestListBox(array, enabledArray, selectedArray);
1972        }
1973    }
1974
1975    // called by JNI
1976    private void requestListBox(String[] array, boolean[] enabledArray,
1977            int selection) {
1978        if (mWebView != null) {
1979            mWebView.requestListBox(array, enabledArray, selection);
1980        }
1981
1982    }
1983
1984    // called by JNI
1985    private void requestKeyboard(boolean showKeyboard) {
1986        if (mWebView != null) {
1987            Message.obtain(mWebView.mPrivateHandler,
1988                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
1989                    .sendToTarget();
1990        }
1991    }
1992
1993    // This class looks like a SurfaceView to native code. In java, we can
1994    // assume the passed in SurfaceView is this class so we can talk to the
1995    // ViewManager through the ChildView.
1996    private class SurfaceViewProxy extends SurfaceView
1997            implements SurfaceHolder.Callback {
1998        private final ViewManager.ChildView mChildView;
1999        private int mPointer;
2000        private final boolean mIsFixedSize;
2001        SurfaceViewProxy(Context context, ViewManager.ChildView childView,
2002                int pointer, int pixelFormat, boolean isFixedSize) {
2003            super(context);
2004            setWillNotDraw(false); // this prevents the black box artifact
2005            getHolder().addCallback(this);
2006            getHolder().setFormat(pixelFormat);
2007            mChildView = childView;
2008            mChildView.mView = this;
2009            mPointer = pointer;
2010            mIsFixedSize = isFixedSize;
2011        }
2012        void destroy() {
2013            mPointer = 0;
2014            mChildView.removeView();
2015        }
2016        void attach(int x, int y, int width, int height) {
2017            mChildView.attachView(x, y, width, height);
2018
2019            if (mIsFixedSize) {
2020                getHolder().setFixedSize(width, height);
2021            }
2022        }
2023
2024        // SurfaceHolder.Callback methods
2025        public void surfaceCreated(SurfaceHolder holder) {
2026            if (mPointer != 0) {
2027                nativeSurfaceChanged(mPointer, 0, 0, 0, 0);
2028            }
2029        }
2030        public void surfaceChanged(SurfaceHolder holder, int format, int width,
2031                int height) {
2032            if (mPointer != 0) {
2033                nativeSurfaceChanged(mPointer, 1, format, width, height);
2034            }
2035        }
2036        public void surfaceDestroyed(SurfaceHolder holder) {
2037            if (mPointer != 0) {
2038                nativeSurfaceChanged(mPointer, 2, 0, 0, 0);
2039            }
2040        }
2041    }
2042
2043    // PluginWidget functions for mainting SurfaceViews for the Surface drawing
2044    // model.
2045    private SurfaceView createSurface(int nativePointer, int pixelFormat,
2046                                      boolean isFixedSize) {
2047        if (mWebView == null) {
2048            return null;
2049        }
2050        return new SurfaceViewProxy(mContext, mWebView.mViewManager.createView(),
2051                                    nativePointer, pixelFormat, isFixedSize);
2052    }
2053
2054    private void destroySurface(SurfaceView surface) {
2055        SurfaceViewProxy proxy = (SurfaceViewProxy) surface;
2056        proxy.destroy();
2057    }
2058
2059    private void attachSurface(SurfaceView surface, int x, int y,
2060            int width, int height) {
2061        SurfaceViewProxy proxy = (SurfaceViewProxy) surface;
2062        proxy.attach(x, y, width, height);
2063    }
2064
2065    // Callback for the SurfaceHolder.Callback. Called for all the surface
2066    // callbacks. The state parameter is one of Created(0), Changed(1),
2067    // Destroyed(2).
2068    private native void nativeSurfaceChanged(int pointer, int state, int format,
2069            int width, int height);
2070
2071    private native void nativePause();
2072    private native void nativeResume();
2073    private native void nativeFreeMemory();
2074}
2075