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