OSDN Git Service

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