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