WebViewCore.java revision 0d74112eed94c98ccaf4b192d933bc1f931675f1
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.app.ActivityManager;
20import android.content.Context;
21import android.content.pm.PackageManager.NameNotFoundException;
22import android.database.Cursor;
23import android.graphics.Point;
24import android.graphics.Rect;
25import android.graphics.Region;
26import android.media.MediaFile;
27import android.net.Uri;
28import android.os.Handler;
29import android.os.Looper;
30import android.os.Message;
31import android.os.Process;
32import android.provider.MediaStore;
33import android.util.Log;
34import android.util.SparseBooleanArray;
35import android.view.KeyEvent;
36import android.view.MotionEvent;
37import android.view.SurfaceView;
38import android.view.View;
39import android.webkit.DeviceMotionService;
40import android.webkit.DeviceMotionAndOrientationManager;
41import android.webkit.DeviceOrientationService;
42import android.webkit.JniUtil;
43
44import java.util.ArrayList;
45import java.util.Collection;
46import java.util.Map;
47import java.util.Set;
48
49import junit.framework.Assert;
50
51/**
52 * @hide
53 */
54public final class WebViewCore {
55
56    private static final String LOGTAG = "webcore";
57
58    static {
59        // Load libwebcore during static initialization. This happens in the
60        // zygote process so it will be shared read-only across all app
61        // processes.
62        try {
63            System.loadLibrary("webcore");
64        } catch (UnsatisfiedLinkError e) {
65            Log.e(LOGTAG, "Unable to load webcore library");
66        }
67    }
68
69    /*
70     * WebViewCore always executes in the same thread as the native webkit.
71     */
72
73    // The WebView that corresponds to this WebViewCore.
74    private WebView mWebView;
75    // Proxy for handling callbacks from native code
76    private final CallbackProxy mCallbackProxy;
77    // Settings object for maintaining all settings
78    private final WebSettings mSettings;
79    // Context for initializing the BrowserFrame with the proper assets.
80    private final Context mContext;
81    // The pointer to a native view object.
82    private int mNativeClass;
83    // The BrowserFrame is an interface to the native Frame component.
84    private BrowserFrame mBrowserFrame;
85    // Custom JS interfaces to add during the initialization.
86    private Map<String, Object> mJavascriptInterfaces;
87    /*
88     * range is from 200 to 10,000. 0 is a special value means device-width. -1
89     * means undefined.
90     */
91    private int mViewportWidth = -1;
92
93    /*
94     * range is from 200 to 10,000. 0 is a special value means device-height. -1
95     * means undefined.
96     */
97    private int mViewportHeight = -1;
98
99    /*
100     * scale in percent, range is from 1 to 1000. 0 means undefined.
101     */
102    private int mViewportInitialScale = 0;
103
104    /*
105     * scale in percent, range is from 1 to 1000. 0 means undefined.
106     */
107    private int mViewportMinimumScale = 0;
108
109    /*
110     * scale in percent, range is from 1 to 1000. 0 means undefined.
111     */
112    private int mViewportMaximumScale = 0;
113
114    private boolean mViewportUserScalable = true;
115
116    /*
117     * range is from 70 to 400.
118     * 0 is a special value means device-dpi. The default scale factor will be
119     * always 100.
120     * -1 means undefined. The default scale factor will be
121     * WebView.DEFAULT_SCALE_PERCENT.
122     */
123    private int mViewportDensityDpi = -1;
124
125    private float mRestoredScale = 0;
126    private float mRestoredTextWrapScale = 0;
127    private int mRestoredX = 0;
128    private int mRestoredY = 0;
129
130    private DeviceMotionAndOrientationManager mDeviceMotionAndOrientationManager =
131            new DeviceMotionAndOrientationManager(this);
132    private DeviceMotionService mDeviceMotionService;
133    private DeviceOrientationService mDeviceOrientationService;
134
135    private int mLowMemoryUsageThresholdMb;
136    private int mHighMemoryUsageThresholdMb;
137    private int mHighUsageDeltaMb;
138
139    // The thread name used to identify the WebCore thread and for use in
140    // debugging other classes that require operation within the WebCore thread.
141    /* package */ static final String THREAD_NAME = "WebViewCoreThread";
142
143    public WebViewCore(Context context, WebView w, CallbackProxy proxy,
144            Map<String, Object> javascriptInterfaces) {
145        // No need to assign this in the WebCore thread.
146        mCallbackProxy = proxy;
147        mWebView = w;
148        mJavascriptInterfaces = javascriptInterfaces;
149        // This context object is used to initialize the WebViewCore during
150        // subwindow creation.
151        mContext = context;
152
153        // We need to wait for the initial thread creation before sending
154        // a message to the WebCore thread.
155        // XXX: This is the only time the UI thread will wait for the WebCore
156        // thread!
157        synchronized (WebViewCore.class) {
158            if (sWebCoreHandler == null) {
159                // Create a global thread and start it.
160                Thread t = new Thread(new WebCoreThread());
161                t.setName(THREAD_NAME);
162                t.start();
163                try {
164                    WebViewCore.class.wait();
165                } catch (InterruptedException e) {
166                    Log.e(LOGTAG, "Caught exception while waiting for thread " +
167                           "creation.");
168                    Log.e(LOGTAG, Log.getStackTraceString(e));
169                }
170            }
171        }
172        // Create an EventHub to handle messages before and after the thread is
173        // ready.
174        mEventHub = new EventHub();
175        // Create a WebSettings object for maintaining all settings
176        mSettings = new WebSettings(mContext, mWebView);
177        // The WebIconDatabase needs to be initialized within the UI thread so
178        // just request the instance here.
179        WebIconDatabase.getInstance();
180        // Create the WebStorage singleton and the UI handler
181        WebStorage.getInstance().createUIHandler();
182        // Create the UI handler for GeolocationPermissions
183        GeolocationPermissions.getInstance().createUIHandler();
184
185        // Get the memory class of the current device. V8 will use these values
186        // to GC more effectively.
187        ActivityManager manager = (ActivityManager) mContext.getSystemService(
188                Context.ACTIVITY_SERVICE);
189        ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
190        manager.getMemoryInfo(memInfo);
191
192        // Allow us to use up to our memory class value before V8's GC kicks in.
193        // These values have been determined by experimentation.
194        mLowMemoryUsageThresholdMb = manager.getLargeMemoryClass();
195        mHighMemoryUsageThresholdMb = (int) (mLowMemoryUsageThresholdMb * 1.5);
196        // Avoid constant V8 GC when memory usage equals to working set estimate.
197        mHighUsageDeltaMb = mLowMemoryUsageThresholdMb / 32;
198
199        // Send a message to initialize the WebViewCore.
200        Message init = sWebCoreHandler.obtainMessage(
201                WebCoreThread.INITIALIZE, this);
202        sWebCoreHandler.sendMessage(init);
203    }
204
205    /* Initialize private data within the WebCore thread.
206     */
207    private void initialize() {
208        /* Initialize our private BrowserFrame class to handle all
209         * frame-related functions. We need to create a new view which
210         * in turn creates a C level FrameView and attaches it to the frame.
211         */
212        mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy,
213                mSettings, mJavascriptInterfaces);
214        mJavascriptInterfaces = null;
215        // Sync the native settings and also create the WebCore thread handler.
216        mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
217        // Create the handler and transfer messages for the IconDatabase
218        WebIconDatabase.getInstance().createHandler();
219        // Create the handler for WebStorage
220        WebStorage.getInstance().createHandler();
221        // Create the handler for GeolocationPermissions.
222        GeolocationPermissions.getInstance().createHandler();
223        // The transferMessages call will transfer all pending messages to the
224        // WebCore thread handler.
225        mEventHub.transferMessages();
226
227        // Send a message back to WebView to tell it that we have set up the
228        // WebCore thread.
229        if (mWebView != null) {
230            Message.obtain(mWebView.mPrivateHandler,
231                    WebView.WEBCORE_INITIALIZED_MSG_ID,
232                    mNativeClass, 0).sendToTarget();
233        }
234
235    }
236
237    /* Handle the initialization of WebViewCore during subwindow creation. This
238     * method is called from the WebCore thread but it is called before the
239     * INITIALIZE message can be handled.
240     */
241    /* package */ void initializeSubwindow() {
242        // Go ahead and initialize the core components.
243        initialize();
244        // Remove the INITIALIZE method so we don't try to initialize twice.
245        sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this);
246    }
247
248    /* Get the BrowserFrame component. This is used for subwindow creation and
249     * is called only from BrowserFrame in the WebCore thread. */
250    /* package */ BrowserFrame getBrowserFrame() {
251        return mBrowserFrame;
252    }
253
254    //-------------------------------------------------------------------------
255    // Common methods
256    //-------------------------------------------------------------------------
257
258    /**
259     * Causes all timers to pause. This applies to all WebViews in the current
260     * app process.
261     */
262    public static void pauseTimers() {
263        if (BrowserFrame.sJavaBridge == null) {
264            throw new IllegalStateException(
265                    "No WebView has been created in this process!");
266        }
267        BrowserFrame.sJavaBridge.pause();
268    }
269
270    /**
271     * Resume all timers. This applies to all WebViews in the current process.
272     */
273    public static void resumeTimers() {
274        if (BrowserFrame.sJavaBridge == null) {
275            throw new IllegalStateException(
276                    "No WebView has been created in this process!");
277        }
278        BrowserFrame.sJavaBridge.resume();
279    }
280
281    public WebSettings getSettings() {
282        return mSettings;
283    }
284
285    /*
286     * Given mimeType, check whether it's supported in Android media framework.
287     * mimeType could be such as "audio/ogg" and "video/mp4".
288     */
289    /* package */ static boolean isSupportedMediaMimeType(String mimeType) {
290        int fileType = MediaFile.getFileTypeForMimeType(mimeType);
291        return MediaFile.isAudioFileType(fileType)
292            || MediaFile.isVideoFileType(fileType)
293            || MediaFile.isPlayListFileType(fileType)
294            // The following is not in Media framework, but it's supported.
295            || (mimeType != null && mimeType.startsWith("video/m4v"));
296    }
297
298    /**
299     * Add an error message to the client's console.
300     * @param message The message to add
301     * @param lineNumber the line on which the error occurred
302     * @param sourceID the filename of the source that caused the error.
303     * @param msgLevel the log level of this message. This is a value casted to int
304     *     from WebCore::MessageLevel in WebCore/page/Console.h.
305     */
306    protected void addMessageToConsole(String message, int lineNumber, String sourceID,
307            int msgLevel) {
308        mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID, msgLevel);
309    }
310
311    /**
312     * Invoke a javascript alert.
313     * @param message The message displayed in the alert.
314     */
315    protected void jsAlert(String url, String message) {
316        mCallbackProxy.onJsAlert(url, message);
317    }
318
319    /**
320     * Called by JNI.  Send a message to the UI thread to hide the soft keyboard
321     * if the node pointed to by nodePointer is still in focus.
322     * @param nodePointer The node which just blurred.
323     */
324    private void formDidBlur(int nodePointer) {
325        if (mWebView == null) return;
326        Message.obtain(mWebView.mPrivateHandler, WebView.FORM_DID_BLUR,
327                nodePointer, 0).sendToTarget();
328    }
329
330    /**
331     * Called by JNI.  Open a file chooser to upload a file.
332     * @param acceptType The value of the 'accept' attribute of the
333     *         input tag associated with this file picker.
334     * @return String version of the URI.
335     */
336    private String openFileChooser(String acceptType) {
337        Uri uri = mCallbackProxy.openFileChooser(acceptType);
338        if (uri != null) {
339            String filePath = "";
340            // Note - querying for MediaStore.Images.Media.DATA
341            // seems to work for all content URIs, not just images
342            Cursor cursor = mContext.getContentResolver().query(
343                    uri,
344                    new String[] { MediaStore.Images.Media.DATA },
345                    null, null, null);
346            if (cursor != null) {
347                try {
348                    if (cursor.moveToNext()) {
349                        filePath = cursor.getString(0);
350                    }
351                } finally {
352                    cursor.close();
353                }
354            } else {
355                filePath = uri.getLastPathSegment();
356            }
357            String uriString = uri.toString();
358            BrowserFrame.sJavaBridge.storeFilePathForContentUri(filePath, uriString);
359            return uriString;
360        }
361        return "";
362    }
363
364    /**
365     * Notify the browser that the origin has exceeded it's database quota.
366     * @param url The URL that caused the overflow.
367     * @param databaseIdentifier The identifier of the database.
368     * @param currentQuota The current quota for the origin.
369     * @param estimatedSize The estimated size of the database.
370     */
371    protected void exceededDatabaseQuota(String url,
372                                         String databaseIdentifier,
373                                         long currentQuota,
374                                         long estimatedSize) {
375        // Inform the callback proxy of the quota overflow. Send an object
376        // that encapsulates a call to the nativeSetDatabaseQuota method to
377        // awaken the sleeping webcore thread when a decision from the
378        // client to allow or deny quota is available.
379        mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
380                currentQuota, estimatedSize, getUsedQuota(),
381                new WebStorage.QuotaUpdater() {
382                        public void updateQuota(long quota) {
383                            nativeSetNewStorageLimit(quota);
384                        }
385                });
386    }
387
388    /**
389     * Notify the browser that the appcache has exceeded its max size.
390     * @param spaceNeeded is the amount of disk space that would be needed
391     * in order for the last appcache operation to succeed.
392     */
393    protected void reachedMaxAppCacheSize(long spaceNeeded) {
394        mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(),
395                new WebStorage.QuotaUpdater() {
396                    public void updateQuota(long quota) {
397                        nativeSetNewStorageLimit(quota);
398                    }
399                });
400    }
401
402    protected void populateVisitedLinks() {
403        ValueCallback callback = new ValueCallback<String[]>() {
404            public void onReceiveValue(String[] value) {
405                sendMessage(EventHub.POPULATE_VISITED_LINKS, (Object)value);
406            }
407        };
408        mCallbackProxy.getVisitedHistory(callback);
409    }
410
411    /**
412     * Shows a prompt to ask the user to set the Geolocation permission state
413     * for the given origin.
414     * @param origin The origin for which Geolocation permissions are
415     *     requested.
416     */
417    protected void geolocationPermissionsShowPrompt(String origin) {
418        mCallbackProxy.onGeolocationPermissionsShowPrompt(origin,
419                new GeolocationPermissions.Callback() {
420          public void invoke(String origin, boolean allow, boolean remember) {
421            GeolocationPermissionsData data = new GeolocationPermissionsData();
422            data.mOrigin = origin;
423            data.mAllow = allow;
424            data.mRemember = remember;
425            // Marshall to WebCore thread.
426            sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data);
427          }
428        });
429    }
430
431    /**
432     * Hides the Geolocation permissions prompt.
433     */
434    protected void geolocationPermissionsHidePrompt() {
435        mCallbackProxy.onGeolocationPermissionsHidePrompt();
436    }
437
438    /**
439     * Invoke a javascript confirm dialog.
440     * @param message The message displayed in the dialog.
441     * @return True if the user confirmed or false if the user cancelled.
442     */
443    protected boolean jsConfirm(String url, String message) {
444        return mCallbackProxy.onJsConfirm(url, message);
445    }
446
447    /**
448     * Invoke a javascript prompt dialog.
449     * @param message The message to be displayed in the dialog.
450     * @param defaultValue The default value in the prompt input.
451     * @return The input from the user or null to indicate the user cancelled
452     *         the dialog.
453     */
454    protected String jsPrompt(String url, String message, String defaultValue) {
455        return mCallbackProxy.onJsPrompt(url, message, defaultValue);
456    }
457
458    /**
459     * Invoke a javascript before unload dialog.
460     * @param url The url that is requesting the dialog.
461     * @param message The message displayed in the dialog.
462     * @return True if the user confirmed or false if the user cancelled. False
463     *         will cancel the navigation.
464     */
465    protected boolean jsUnload(String url, String message) {
466        return mCallbackProxy.onJsBeforeUnload(url, message);
467    }
468
469    /**
470     *
471     * Callback to notify that a JavaScript execution timeout has occured.
472     * @return True if the JavaScript execution should be interrupted. False
473     *         will continue the execution.
474     */
475    protected boolean jsInterrupt() {
476        return mCallbackProxy.onJsTimeout();
477    }
478
479    /**
480     * Notify the webview that this is an installable web app.
481     */
482    protected void setInstallableWebApp() {
483        mCallbackProxy.setInstallableWebApp();
484    }
485
486    /**
487     * Notify the webview that we want to display the video layer fullscreen.
488     */
489    protected void enterFullscreenForVideoLayer(int layerId, String url) {
490        if (mWebView == null) return;
491        Message message = Message.obtain(mWebView.mPrivateHandler,
492                       WebView.ENTER_FULLSCREEN_VIDEO, layerId, 0);
493        message.obj = url;
494        message.sendToTarget();
495    }
496
497    //-------------------------------------------------------------------------
498    // JNI methods
499    //-------------------------------------------------------------------------
500
501    static native String nativeFindAddress(String addr, boolean caseInsensitive);
502
503    /**
504     * Empty the picture set.
505     */
506    private native void nativeClearContent();
507
508    private native void nativeContentInvalidateAll();
509
510    /**
511     * Redraw a portion of the picture set. The Point wh returns the
512     * width and height of the overall picture.
513     */
514    private native int nativeRecordContent(Region invalRegion, Point wh);
515
516    /**
517     * Update the layers' content
518     */
519    private native int nativeUpdateLayers(Region invalRegion);
520
521    private native boolean nativeFocusBoundsChanged();
522
523    /**
524     * Splits slow parts of the picture set. Called from the webkit thread after
525     * WebView.nativeDraw() returns content to be split.
526     */
527    private native void nativeSplitContent(int content);
528
529    private native boolean nativeKey(int keyCode, int unichar,
530            int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
531            boolean isDown);
532
533    private native void nativeClick(int framePtr, int nodePtr, boolean fake);
534
535    private native void nativeSendListBoxChoices(boolean[] choices, int size);
536
537    private native void nativeSendListBoxChoice(int choice);
538
539    /*  Tell webkit what its width and height are, for the purposes
540        of layout/line-breaking. These coordinates are in document space,
541        which is the same as View coords unless we have zoomed the document
542        (see nativeSetZoom).
543        textWrapWidth is used by layout to wrap column around. If viewport uses
544        fixed size, textWrapWidth can be different from width with zooming.
545        should this be called nativeSetViewPortSize?
546    */
547    private native void nativeSetSize(int width, int height, int textWrapWidth,
548            float scale, int screenWidth, int screenHeight, int anchorX,
549            int anchorY, boolean ignoreHeight);
550
551    private native int nativeGetContentMinPrefWidth();
552
553    // Start: functions that deal with text editing
554    private native void nativeReplaceTextfieldText(
555            int oldStart, int oldEnd, String replace, int newStart, int newEnd,
556            int textGeneration);
557
558    private native void passToJs(int gen,
559            String currentText, int keyCode, int keyValue, boolean down,
560            boolean cap, boolean fn, boolean sym);
561
562    private native void nativeSetFocusControllerActive(boolean active);
563
564    private native void nativeSaveDocumentState(int frame);
565
566    private native void nativeMoveFocus(int framePtr, int nodePointer);
567    private native void nativeMoveMouse(int framePtr, int x, int y);
568
569    private native void nativeMoveMouseIfLatest(int moveGeneration,
570            int framePtr, int x, int y);
571
572    private native String nativeRetrieveHref(int x, int y);
573    private native String nativeRetrieveAnchorText(int x, int y);
574    private native String nativeRetrieveImageSource(int x, int y);
575    private native void nativeStopPaintingCaret();
576    private native void nativeTouchUp(int touchGeneration,
577            int framePtr, int nodePtr, int x, int y);
578
579    private native boolean nativeHandleTouchEvent(int action, int[] idArray,
580            int[] xArray, int[] yArray, int count, int actionIndex, int metaState);
581
582    private native void nativeUpdateFrameCache();
583
584    private native void nativeSetBackgroundColor(int color);
585
586    private native void nativeDumpDomTree(boolean useFile);
587
588    private native void nativeDumpRenderTree(boolean useFile);
589
590    private native void nativeDumpNavTree();
591
592    private native void nativeDumpV8Counters();
593
594    private native void nativeSetJsFlags(String flags);
595
596    /**
597     *  Delete text from start to end in the focused textfield. If there is no
598     *  focus, or if start == end, silently fail.  If start and end are out of
599     *  order, swap them.
600     *  @param  start   Beginning of selection to delete.
601     *  @param  end     End of selection to delete.
602     *  @param  textGeneration Text generation number when delete was pressed.
603     */
604    private native void nativeDeleteSelection(int start, int end,
605            int textGeneration);
606
607    /**
608     *  Set the selection to (start, end) in the focused textfield. If start and
609     *  end are out of order, swap them.
610     *  @param  start   Beginning of selection.
611     *  @param  end     End of selection.
612     */
613    private native void nativeSetSelection(int start, int end);
614
615    // Register a scheme to be treated as local scheme so that it can access
616    // local asset files for resources
617    private native void nativeRegisterURLSchemeAsLocal(String scheme);
618
619    /*
620     * Inform webcore that the user has decided whether to allow or deny new
621     * quota for the current origin or more space for the app cache, and that
622     * the main thread should wake up now.
623     * @param limit Is the new quota for an origin or new app cache max size.
624     */
625    private native void nativeSetNewStorageLimit(long limit);
626
627    /**
628     * Provide WebCore with a Geolocation permission state for the specified
629     * origin.
630     * @param origin The origin for which Geolocation permissions are provided.
631     * @param allow Whether Geolocation permissions are allowed.
632     * @param remember Whether this decision should be remembered beyond the
633     *     life of the current page.
634     */
635    private native void nativeGeolocationPermissionsProvide(String origin, boolean allow, boolean remember);
636
637    /**
638     * Provide WebCore with the previously visted links from the history database
639     */
640    private native void nativeProvideVisitedHistory(String[] history);
641
642    /**
643     * Modifies the current selection.
644     *
645     * Note: Accessibility support.
646     *
647     * @param direction The direction in which to alter the selection.
648     * @param granularity The granularity of the selection modification.
649     *
650     * @return The selection string.
651     */
652    private native String nativeModifySelection(int direction, int granularity);
653
654    // EventHub for processing messages
655    private final EventHub mEventHub;
656    // WebCore thread handler
657    private static Handler sWebCoreHandler;
658    // Class for providing Handler creation inside the WebCore thread.
659    private static class WebCoreThread implements Runnable {
660        // Message id for initializing a new WebViewCore.
661        private static final int INITIALIZE = 0;
662        private static final int REDUCE_PRIORITY = 1;
663        private static final int RESUME_PRIORITY = 2;
664
665        public void run() {
666            Looper.prepare();
667            Assert.assertNull(sWebCoreHandler);
668            synchronized (WebViewCore.class) {
669                sWebCoreHandler = new Handler() {
670                    @Override
671                    public void handleMessage(Message msg) {
672                        switch (msg.what) {
673                            case INITIALIZE:
674                                WebViewCore core = (WebViewCore) msg.obj;
675                                core.initialize();
676                                break;
677
678                            case REDUCE_PRIORITY:
679                                // 3 is an adjustable number.
680                                Process.setThreadPriority(
681                                        Process.THREAD_PRIORITY_DEFAULT + 3 *
682                                        Process.THREAD_PRIORITY_LESS_FAVORABLE);
683                                break;
684
685                            case RESUME_PRIORITY:
686                                Process.setThreadPriority(
687                                        Process.THREAD_PRIORITY_DEFAULT);
688                                break;
689
690                            case EventHub.ADD_PACKAGE_NAME:
691                                if (BrowserFrame.sJavaBridge == null) {
692                                    throw new IllegalStateException(
693                                            "No WebView has been created in this process!");
694                                }
695                                BrowserFrame.sJavaBridge.addPackageName((String) msg.obj);
696                                break;
697
698                            case EventHub.REMOVE_PACKAGE_NAME:
699                                if (BrowserFrame.sJavaBridge == null) {
700                                    throw new IllegalStateException(
701                                            "No WebView has been created in this process!");
702                                }
703                                BrowserFrame.sJavaBridge.removePackageName((String) msg.obj);
704                                break;
705
706                            case EventHub.PROXY_CHANGED:
707                                if (BrowserFrame.sJavaBridge == null) {
708                                    throw new IllegalStateException(
709                                            "No WebView has been created in this process!");
710                                }
711                                BrowserFrame.sJavaBridge.updateProxy((String) msg.obj);
712                                break;
713                        }
714                    }
715                };
716                WebViewCore.class.notify();
717            }
718            Looper.loop();
719        }
720    }
721
722    static class BaseUrlData {
723        String mBaseUrl;
724        String mData;
725        String mMimeType;
726        String mEncoding;
727        String mHistoryUrl;
728    }
729
730    static class CursorData {
731        CursorData() {}
732        CursorData(int frame, int node, int x, int y) {
733            mFrame = frame;
734            mNode = node;
735            mX = x;
736            mY = y;
737        }
738        int mMoveGeneration;
739        int mFrame;
740        int mNode;
741        int mX;
742        int mY;
743    }
744
745    static class JSInterfaceData {
746        Object mObject;
747        String mInterfaceName;
748    }
749
750    static class JSKeyData {
751        String mCurrentText;
752        KeyEvent mEvent;
753    }
754
755    static class MotionUpData {
756        int mFrame;
757        int mNode;
758        Rect mBounds;
759        int mX;
760        int mY;
761    }
762
763    static class GetUrlData {
764        String mUrl;
765        Map<String, String> mExtraHeaders;
766    }
767
768    static class PostUrlData {
769        String mUrl;
770        byte[] mPostData;
771    }
772
773    static class ReplaceTextData {
774        String mReplace;
775        int mNewStart;
776        int mNewEnd;
777        int mTextGeneration;
778    }
779
780    static class TextSelectionData {
781        public TextSelectionData(int start, int end) {
782            mStart = start;
783            mEnd = end;
784        }
785        int mStart;
786        int mEnd;
787    }
788
789    static class TouchUpData {
790        int mMoveGeneration;
791        int mFrame;
792        int mNode;
793        int mX;
794        int mY;
795        int mNativeLayer;
796        Rect mNativeLayerRect = new Rect();
797    }
798
799    static class TouchHighlightData {
800        int mX;
801        int mY;
802        int mSlop;
803    }
804
805    static class AutoFillData {
806        public AutoFillData() {
807            mQueryId = WebTextView.FORM_NOT_AUTOFILLABLE;
808            mPreview = "";
809        }
810
811        public AutoFillData(int queryId, String preview) {
812            mQueryId = queryId;
813            mPreview = preview;
814        }
815
816        public int getQueryId() {
817            return mQueryId;
818        }
819
820        public String getPreviewString() {
821            return mPreview;
822        }
823
824        private int mQueryId;
825        private String mPreview;
826    }
827
828    // mAction of TouchEventData can be MotionEvent.getAction() which uses the
829    // last two bytes or one of the following values
830    static final int ACTION_LONGPRESS = 0x100;
831    static final int ACTION_DOUBLETAP = 0x200;
832
833    static class TouchEventData {
834        int mAction;
835        int[] mIds;  // Ids of the touch points
836        Point[] mPoints;
837        Point[] mPointsInView;  // the point coordinates in view axis.
838        int mActionIndex;  // Associated pointer index for ACTION_POINTER_DOWN/UP
839        int mMetaState;
840        boolean mReprocess;
841        MotionEvent mMotionEvent;
842        int mNativeLayer;
843        Rect mNativeLayerRect = new Rect();
844        long mSequence;
845        boolean mNativeResult;
846    }
847
848    static class GeolocationPermissionsData {
849        String mOrigin;
850        boolean mAllow;
851        boolean mRemember;
852    }
853
854        static final String[] HandlerDebugString = {
855            "REVEAL_SELECTION", // 96
856            "REQUEST_LABEL", // 97
857            "UPDATE_FRAME_CACHE_IF_LOADING", // = 98
858            "SCROLL_TEXT_INPUT", // = 99
859            "LOAD_URL", // = 100;
860            "STOP_LOADING", // = 101;
861            "RELOAD", // = 102;
862            "KEY_DOWN", // = 103;
863            "KEY_UP", // = 104;
864            "VIEW_SIZE_CHANGED", // = 105;
865            "GO_BACK_FORWARD", // = 106;
866            "SET_SCROLL_OFFSET", // = 107;
867            "RESTORE_STATE", // = 108;
868            "PAUSE_TIMERS", // = 109;
869            "RESUME_TIMERS", // = 110;
870            "CLEAR_CACHE", // = 111;
871            "CLEAR_HISTORY", // = 112;
872            "SET_SELECTION", // = 113;
873            "REPLACE_TEXT", // = 114;
874            "PASS_TO_JS", // = 115;
875            "SET_GLOBAL_BOUNDS", // = 116;
876            "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117;
877            "CLICK", // = 118;
878            "SET_NETWORK_STATE", // = 119;
879            "DOC_HAS_IMAGES", // = 120;
880            "FAKE_CLICK", // = 121;
881            "DELETE_SELECTION", // = 122;
882            "LISTBOX_CHOICES", // = 123;
883            "SINGLE_LISTBOX_CHOICE", // = 124;
884            "MESSAGE_RELAY", // = 125;
885            "SET_BACKGROUND_COLOR", // = 126;
886            "SET_MOVE_FOCUS", // = 127
887            "SAVE_DOCUMENT_STATE", // = 128;
888            "129", // = 129;
889            "WEBKIT_DRAW", // = 130;
890            "131", // = 131;
891            "POST_URL", // = 132;
892            "SPLIT_PICTURE_SET", // = 133;
893            "CLEAR_CONTENT", // = 134;
894            "SET_MOVE_MOUSE", // = 135;
895            "SET_MOVE_MOUSE_IF_LATEST", // = 136;
896            "REQUEST_CURSOR_HREF", // = 137;
897            "ADD_JS_INTERFACE", // = 138;
898            "LOAD_DATA", // = 139;
899            "TOUCH_UP", // = 140;
900            "TOUCH_EVENT", // = 141;
901            "SET_ACTIVE", // = 142;
902            "ON_PAUSE",     // = 143
903            "ON_RESUME",    // = 144
904            "FREE_MEMORY",  // = 145
905            "VALID_NODE_BOUNDS", // = 146
906            "SAVE_WEBARCHIVE", // = 147
907            "WEBKIT_DRAW_LAYERS", // = 148;
908            "REMOVE_JS_INTERFACE", // = 149;
909        };
910
911    /**
912     * @hide
913     */
914    public class EventHub {
915        // Message Ids
916        static final int REVEAL_SELECTION = 96;
917        static final int REQUEST_LABEL = 97;
918        static final int UPDATE_FRAME_CACHE_IF_LOADING = 98;
919        static final int SCROLL_TEXT_INPUT = 99;
920        static final int LOAD_URL = 100;
921        static final int STOP_LOADING = 101;
922        static final int RELOAD = 102;
923        static final int KEY_DOWN = 103;
924        static final int KEY_UP = 104;
925        static final int VIEW_SIZE_CHANGED = 105;
926        static final int GO_BACK_FORWARD = 106;
927        static final int SET_SCROLL_OFFSET = 107;
928        static final int RESTORE_STATE = 108;
929        static final int PAUSE_TIMERS = 109;
930        static final int RESUME_TIMERS = 110;
931        static final int CLEAR_CACHE = 111;
932        static final int CLEAR_HISTORY = 112;
933        static final int SET_SELECTION = 113;
934        static final int REPLACE_TEXT = 114;
935        static final int PASS_TO_JS = 115;
936        static final int SET_GLOBAL_BOUNDS = 116;
937        static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117;
938        static final int CLICK = 118;
939        static final int SET_NETWORK_STATE = 119;
940        static final int DOC_HAS_IMAGES = 120;
941        static final int FAKE_CLICK = 121;
942        static final int DELETE_SELECTION = 122;
943        static final int LISTBOX_CHOICES = 123;
944        static final int SINGLE_LISTBOX_CHOICE = 124;
945        public static final int MESSAGE_RELAY = 125;
946        static final int SET_BACKGROUND_COLOR = 126;
947        static final int SET_MOVE_FOCUS = 127;
948        static final int SAVE_DOCUMENT_STATE = 128;
949
950        static final int WEBKIT_DRAW = 130;
951        static final int POST_URL = 132;
952        static final int SPLIT_PICTURE_SET = 133;
953        static final int CLEAR_CONTENT = 134;
954
955        // UI nav messages
956        static final int SET_MOVE_MOUSE = 135;
957        static final int SET_MOVE_MOUSE_IF_LATEST = 136;
958        static final int REQUEST_CURSOR_HREF = 137;
959        static final int ADD_JS_INTERFACE = 138;
960        static final int LOAD_DATA = 139;
961
962        // motion
963        static final int TOUCH_UP = 140;
964        // message used to pass UI touch events to WebCore
965        static final int TOUCH_EVENT = 141;
966
967        // Used to tell the focus controller not to draw the blinking cursor,
968        // based on whether the WebView has focus and whether the WebView's
969        // cursor matches the webpage's focus.
970        static final int SET_ACTIVE = 142;
971
972        // lifecycle activities for just this DOM (unlike pauseTimers, which
973        // is global)
974        static final int ON_PAUSE = 143;
975        static final int ON_RESUME = 144;
976        static final int FREE_MEMORY = 145;
977        static final int VALID_NODE_BOUNDS = 146;
978
979        // Load and save web archives
980        static final int SAVE_WEBARCHIVE = 147;
981
982        // Update layers
983        static final int WEBKIT_DRAW_LAYERS = 148;
984
985        static final int REMOVE_JS_INTERFACE = 149;
986
987        // Network-based messaging
988        static final int CLEAR_SSL_PREF_TABLE = 150;
989
990        // Test harness messages
991        static final int REQUEST_EXT_REPRESENTATION = 160;
992        static final int REQUEST_DOC_AS_TEXT = 161;
993
994        // debugging
995        static final int DUMP_DOMTREE = 170;
996        static final int DUMP_RENDERTREE = 171;
997        static final int DUMP_NAVTREE = 172;
998        static final int DUMP_V8COUNTERS = 173;
999
1000        static final int SET_JS_FLAGS = 174;
1001        // Geolocation
1002        static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180;
1003
1004        static final int POPULATE_VISITED_LINKS = 181;
1005
1006        static final int HIDE_FULLSCREEN = 182;
1007
1008        static final int SET_NETWORK_TYPE = 183;
1009
1010        // navigator.isApplicationInstalled()
1011        static final int ADD_PACKAGE_NAMES = 184;
1012        static final int ADD_PACKAGE_NAME = 185;
1013        static final int REMOVE_PACKAGE_NAME = 186;
1014
1015        static final int GET_TOUCH_HIGHLIGHT_RECTS = 187;
1016        static final int REMOVE_TOUCH_HIGHLIGHT_RECTS = 188;
1017
1018        // accessibility support
1019        static final int MODIFY_SELECTION = 190;
1020
1021        static final int USE_MOCK_DEVICE_ORIENTATION = 191;
1022
1023        static final int AUTOFILL_FORM = 192;
1024
1025        static final int PROXY_CHANGED = 193;
1026
1027        static final int EXECUTE_JS = 194;
1028
1029        // private message ids
1030        private static final int DESTROY =     200;
1031
1032        // Private handler for WebCore messages.
1033        private Handler mHandler;
1034        // Message queue for containing messages before the WebCore thread is
1035        // ready.
1036        private ArrayList<Message> mMessages = new ArrayList<Message>();
1037        // Flag for blocking messages. This is used during DESTROY to avoid
1038        // posting more messages to the EventHub or to WebView's event handler.
1039        private boolean mBlockMessages;
1040
1041        private int mTid;
1042        private int mSavedPriority;
1043
1044        /**
1045         * Prevent other classes from creating an EventHub.
1046         */
1047        private EventHub() {}
1048
1049        private static final int FIRST_PACKAGE_MSG_ID = REVEAL_SELECTION;
1050        private static final int LAST_PACKAGE_MSG_ID = VALID_NODE_BOUNDS;
1051
1052        /**
1053         * Transfer all messages to the newly created webcore thread handler.
1054         */
1055        private void transferMessages() {
1056            mTid = Process.myTid();
1057            mSavedPriority = Process.getThreadPriority(mTid);
1058
1059            mHandler = new Handler() {
1060                @Override
1061                public void handleMessage(Message msg) {
1062                    if (DebugFlags.WEB_VIEW_CORE) {
1063                        Log.v(LOGTAG, (msg.what < FIRST_PACKAGE_MSG_ID
1064                                || msg.what > LAST_PACKAGE_MSG_ID
1065                                ? Integer.toString(msg.what)
1066                                : HandlerDebugString[msg.what
1067                                        - FIRST_PACKAGE_MSG_ID])
1068                                + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
1069                                + " obj=" + msg.obj);
1070                    }
1071                    switch (msg.what) {
1072                        case WEBKIT_DRAW:
1073                            webkitDraw();
1074                            break;
1075
1076                        case WEBKIT_DRAW_LAYERS:
1077                            webkitDrawLayers();
1078                            break;
1079
1080                        case DESTROY:
1081                            // Time to take down the world. Cancel all pending
1082                            // loads and destroy the native view and frame.
1083                            synchronized (WebViewCore.this) {
1084                                mBrowserFrame.destroy();
1085                                mBrowserFrame = null;
1086                                mSettings.onDestroyed();
1087                                mNativeClass = 0;
1088                                mWebView = null;
1089                            }
1090                            break;
1091
1092                        case REVEAL_SELECTION:
1093                            nativeRevealSelection();
1094                            break;
1095
1096                        case REQUEST_LABEL:
1097                            if (mWebView != null) {
1098                                int nodePointer = msg.arg2;
1099                                String label = nativeRequestLabel(msg.arg1,
1100                                        nodePointer);
1101                                if (label != null && label.length() > 0) {
1102                                    Message.obtain(mWebView.mPrivateHandler,
1103                                            WebView.RETURN_LABEL, nodePointer,
1104                                            0, label).sendToTarget();
1105                                }
1106                            }
1107                            break;
1108
1109                        case UPDATE_FRAME_CACHE_IF_LOADING:
1110                            nativeUpdateFrameCacheIfLoading();
1111                            break;
1112
1113                        case SCROLL_TEXT_INPUT:
1114                            float xPercent;
1115                            if (msg.obj == null) {
1116                                xPercent = 0f;
1117                            } else {
1118                                xPercent = ((Float) msg.obj).floatValue();
1119                            }
1120                            nativeScrollFocusedTextInput(xPercent, msg.arg2);
1121                            break;
1122
1123                        case LOAD_URL: {
1124                            CookieManager.getInstance().waitForCookieOperationsToComplete();
1125                            GetUrlData param = (GetUrlData) msg.obj;
1126                            loadUrl(param.mUrl, param.mExtraHeaders);
1127                            break;
1128                        }
1129
1130                        case POST_URL: {
1131                            CookieManager.getInstance().waitForCookieOperationsToComplete();
1132                            PostUrlData param = (PostUrlData) msg.obj;
1133                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
1134                            break;
1135                        }
1136                        case LOAD_DATA:
1137                            CookieManager.getInstance().waitForCookieOperationsToComplete();
1138                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
1139                            String baseUrl = loadParams.mBaseUrl;
1140                            if (baseUrl != null) {
1141                                int i = baseUrl.indexOf(':');
1142                                if (i > 0) {
1143                                    /*
1144                                     * In 1.0, {@link
1145                                     * WebView#loadDataWithBaseURL} can access
1146                                     * local asset files as long as the data is
1147                                     * valid. In the new WebKit, the restriction
1148                                     * is tightened. To be compatible with 1.0,
1149                                     * we automatically add the scheme of the
1150                                     * baseUrl for local access as long as it is
1151                                     * not http(s)/ftp(s)/about/javascript
1152                                     */
1153                                    String scheme = baseUrl.substring(0, i);
1154                                    if (!scheme.startsWith("http") &&
1155                                            !scheme.startsWith("ftp") &&
1156                                            !scheme.startsWith("about") &&
1157                                            !scheme.startsWith("javascript")) {
1158                                        nativeRegisterURLSchemeAsLocal(scheme);
1159                                    }
1160                                }
1161                            }
1162                            mBrowserFrame.loadData(baseUrl,
1163                                    loadParams.mData,
1164                                    loadParams.mMimeType,
1165                                    loadParams.mEncoding,
1166                                    loadParams.mHistoryUrl);
1167                            break;
1168
1169                        case STOP_LOADING:
1170                            // If the WebCore has committed the load, but not
1171                            // finished the first layout yet, we need to set
1172                            // first layout done to trigger the interpreted side sync
1173                            // up with native side
1174                            if (mBrowserFrame.committed()
1175                                    && !mBrowserFrame.firstLayoutDone()) {
1176                                mBrowserFrame.didFirstLayout();
1177                            }
1178                            // Do this after syncing up the layout state.
1179                            stopLoading();
1180                            break;
1181
1182                        case RELOAD:
1183                            mBrowserFrame.reload(false);
1184                            break;
1185
1186                        case KEY_DOWN:
1187                            key((KeyEvent) msg.obj, true);
1188                            break;
1189
1190                        case KEY_UP:
1191                            key((KeyEvent) msg.obj, false);
1192                            break;
1193
1194                        case FAKE_CLICK:
1195                            nativeClick(msg.arg1, msg.arg2, true);
1196                            break;
1197
1198                        case CLICK:
1199                            nativeClick(msg.arg1, msg.arg2, false);
1200                            break;
1201
1202                        case VIEW_SIZE_CHANGED: {
1203                            viewSizeChanged((WebView.ViewSizeData) msg.obj);
1204                            break;
1205                        }
1206                        case SET_SCROLL_OFFSET:
1207                            // note: these are in document coordinates
1208                            // (inv-zoom)
1209                            Point pt = (Point) msg.obj;
1210                            nativeSetScrollOffset(msg.arg1, msg.arg2 == 1,
1211                                    pt.x, pt.y);
1212                            break;
1213
1214                        case SET_GLOBAL_BOUNDS:
1215                            Rect r = (Rect) msg.obj;
1216                            nativeSetGlobalBounds(r.left, r.top, r.width(),
1217                                r.height());
1218                            break;
1219
1220                        case GO_BACK_FORWARD:
1221                            // If it is a standard load and the load is not
1222                            // committed yet, we interpret BACK as RELOAD
1223                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
1224                                    (mBrowserFrame.loadType() ==
1225                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
1226                                mBrowserFrame.reload(true);
1227                            } else {
1228                                mBrowserFrame.goBackOrForward(msg.arg1);
1229                            }
1230                            break;
1231
1232                        case RESTORE_STATE:
1233                            stopLoading();
1234                            restoreState(msg.arg1);
1235                            break;
1236
1237                        case PAUSE_TIMERS:
1238                            mSavedPriority = Process.getThreadPriority(mTid);
1239                            Process.setThreadPriority(mTid,
1240                                    Process.THREAD_PRIORITY_BACKGROUND);
1241                            pauseTimers();
1242                            if (!JniUtil.useChromiumHttpStack()) {
1243                                WebViewWorker.getHandler().sendEmptyMessage(
1244                                        WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION);
1245                            }
1246                            break;
1247
1248                        case RESUME_TIMERS:
1249                            Process.setThreadPriority(mTid, mSavedPriority);
1250                            resumeTimers();
1251                            if (!JniUtil.useChromiumHttpStack()) {
1252                                WebViewWorker.getHandler().sendEmptyMessage(
1253                                        WebViewWorker.MSG_RESUME_CACHE_TRANSACTION);
1254                            }
1255                            break;
1256
1257                        case ON_PAUSE:
1258                            nativePause();
1259                            break;
1260
1261                        case ON_RESUME:
1262                            nativeResume();
1263                            break;
1264
1265                        case FREE_MEMORY:
1266                            clearCache(false);
1267                            nativeFreeMemory();
1268                            break;
1269
1270                        case SET_NETWORK_STATE:
1271                            if (BrowserFrame.sJavaBridge == null) {
1272                                throw new IllegalStateException("No WebView " +
1273                                        "has been created in this process!");
1274                            }
1275                            BrowserFrame.sJavaBridge
1276                                    .setNetworkOnLine(msg.arg1 == 1);
1277                            break;
1278
1279                        case SET_NETWORK_TYPE:
1280                            if (BrowserFrame.sJavaBridge == null) {
1281                                throw new IllegalStateException("No WebView " +
1282                                        "has been created in this process!");
1283                            }
1284                            Map<String, String> map = (Map<String, String>) msg.obj;
1285                            BrowserFrame.sJavaBridge
1286                                    .setNetworkType(map.get("type"), map.get("subtype"));
1287                            break;
1288
1289                        case CLEAR_CACHE:
1290                            clearCache(msg.arg1 == 1);
1291                            break;
1292
1293                        case CLEAR_HISTORY:
1294                            mCallbackProxy.getBackForwardList().
1295                                    close(mBrowserFrame.mNativeFrame);
1296                            break;
1297
1298                        case REPLACE_TEXT:
1299                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
1300                            nativeReplaceTextfieldText(msg.arg1, msg.arg2,
1301                                    rep.mReplace, rep.mNewStart, rep.mNewEnd,
1302                                    rep.mTextGeneration);
1303                            break;
1304
1305                        case PASS_TO_JS: {
1306                            JSKeyData jsData = (JSKeyData) msg.obj;
1307                            KeyEvent evt = jsData.mEvent;
1308                            int keyCode = evt.getKeyCode();
1309                            int keyValue = evt.getUnicodeChar();
1310                            int generation = msg.arg1;
1311                            passToJs(generation,
1312                                    jsData.mCurrentText,
1313                                    keyCode,
1314                                    keyValue,
1315                                    evt.isDown(),
1316                                    evt.isShiftPressed(), evt.isAltPressed(),
1317                                    evt.isSymPressed());
1318                            break;
1319                        }
1320
1321                        case SAVE_DOCUMENT_STATE: {
1322                            CursorData cDat = (CursorData) msg.obj;
1323                            nativeSaveDocumentState(cDat.mFrame);
1324                            break;
1325                        }
1326
1327                        case CLEAR_SSL_PREF_TABLE:
1328                            Network.getInstance(mContext)
1329                                    .clearUserSslPrefTable();
1330                            break;
1331
1332                        case TOUCH_UP:
1333                            TouchUpData touchUpData = (TouchUpData) msg.obj;
1334                            if (touchUpData.mNativeLayer != 0) {
1335                                nativeScrollLayer(touchUpData.mNativeLayer,
1336                                        touchUpData.mNativeLayerRect);
1337                            }
1338                            nativeTouchUp(touchUpData.mMoveGeneration,
1339                                    touchUpData.mFrame, touchUpData.mNode,
1340                                    touchUpData.mX, touchUpData.mY);
1341                            break;
1342
1343                        case TOUCH_EVENT: {
1344                            TouchEventData ted = (TouchEventData) msg.obj;
1345                            final int count = ted.mPoints.length;
1346                            int[] xArray = new int[count];
1347                            int[] yArray = new int[count];
1348                            for (int c = 0; c < count; c++) {
1349                                xArray[c] = ted.mPoints[c].x;
1350                                yArray[c] = ted.mPoints[c].y;
1351                            }
1352                            if (ted.mNativeLayer != 0) {
1353                                nativeScrollLayer(ted.mNativeLayer,
1354                                        ted.mNativeLayerRect);
1355                            }
1356                            ted.mNativeResult = nativeHandleTouchEvent(ted.mAction, ted.mIds,
1357                                    xArray, yArray, count, ted.mActionIndex, ted.mMetaState);
1358                            Message.obtain(
1359                                    mWebView.mPrivateHandler,
1360                                    WebView.PREVENT_TOUCH_ID,
1361                                    ted.mAction,
1362                                    ted.mNativeResult ? 1 : 0,
1363                                    ted).sendToTarget();
1364                            break;
1365                        }
1366
1367                        case SET_ACTIVE:
1368                            nativeSetFocusControllerActive(msg.arg1 == 1);
1369                            break;
1370
1371                        case ADD_JS_INTERFACE:
1372                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
1373                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
1374                                    jsData.mInterfaceName);
1375                            break;
1376
1377                        case REMOVE_JS_INTERFACE:
1378                            jsData = (JSInterfaceData) msg.obj;
1379                            mBrowserFrame.removeJavascriptInterface(
1380                                    jsData.mInterfaceName);
1381                            break;
1382
1383                        case REQUEST_EXT_REPRESENTATION:
1384                            mBrowserFrame.externalRepresentation(
1385                                    (Message) msg.obj);
1386                            break;
1387
1388                        case REQUEST_DOC_AS_TEXT:
1389                            mBrowserFrame.documentAsText((Message) msg.obj);
1390                            break;
1391
1392                        case SET_MOVE_FOCUS:
1393                            CursorData focusData = (CursorData) msg.obj;
1394                            nativeMoveFocus(focusData.mFrame, focusData.mNode);
1395                            break;
1396
1397                        case SET_MOVE_MOUSE:
1398                            CursorData cursorData = (CursorData) msg.obj;
1399                            nativeMoveMouse(cursorData.mFrame,
1400                                     cursorData.mX, cursorData.mY);
1401                            break;
1402
1403                        case SET_MOVE_MOUSE_IF_LATEST:
1404                            CursorData cData = (CursorData) msg.obj;
1405                            nativeMoveMouseIfLatest(cData.mMoveGeneration,
1406                                    cData.mFrame,
1407                                    cData.mX, cData.mY);
1408                            if (msg.arg1 == 1) {
1409                                nativeStopPaintingCaret();
1410                            }
1411                            break;
1412
1413                        case REQUEST_CURSOR_HREF: {
1414                            Message hrefMsg = (Message) msg.obj;
1415                            hrefMsg.getData().putString("url",
1416                                    nativeRetrieveHref(msg.arg1, msg.arg2));
1417                            hrefMsg.getData().putString("title",
1418                                    nativeRetrieveAnchorText(msg.arg1, msg.arg2));
1419                            hrefMsg.getData().putString("src",
1420                                    nativeRetrieveImageSource(msg.arg1, msg.arg2));
1421                            hrefMsg.sendToTarget();
1422                            break;
1423                        }
1424
1425                        case UPDATE_CACHE_AND_TEXT_ENTRY:
1426                            nativeUpdateFrameCache();
1427                            // FIXME: this should provide a minimal rectangle
1428                            if (mWebView != null) {
1429                                mWebView.postInvalidate();
1430                            }
1431                            sendUpdateTextEntry();
1432                            break;
1433
1434                        case DOC_HAS_IMAGES:
1435                            Message imageResult = (Message) msg.obj;
1436                            imageResult.arg1 =
1437                                    mBrowserFrame.documentHasImages() ? 1 : 0;
1438                            imageResult.sendToTarget();
1439                            break;
1440
1441                        case DELETE_SELECTION:
1442                            TextSelectionData deleteSelectionData
1443                                    = (TextSelectionData) msg.obj;
1444                            nativeDeleteSelection(deleteSelectionData.mStart,
1445                                    deleteSelectionData.mEnd, msg.arg1);
1446                            break;
1447
1448                        case SET_SELECTION:
1449                            nativeSetSelection(msg.arg1, msg.arg2);
1450                            break;
1451
1452                        case MODIFY_SELECTION:
1453                            String modifiedSelectionString = nativeModifySelection(msg.arg1,
1454                                    msg.arg2);
1455                            mWebView.mPrivateHandler.obtainMessage(WebView.SELECTION_STRING_CHANGED,
1456                                    modifiedSelectionString).sendToTarget();
1457                            break;
1458
1459                        case LISTBOX_CHOICES:
1460                            SparseBooleanArray choices = (SparseBooleanArray)
1461                                    msg.obj;
1462                            int choicesSize = msg.arg1;
1463                            boolean[] choicesArray = new boolean[choicesSize];
1464                            for (int c = 0; c < choicesSize; c++) {
1465                                choicesArray[c] = choices.get(c);
1466                            }
1467                            nativeSendListBoxChoices(choicesArray,
1468                                    choicesSize);
1469                            break;
1470
1471                        case SINGLE_LISTBOX_CHOICE:
1472                            nativeSendListBoxChoice(msg.arg1);
1473                            break;
1474
1475                        case SET_BACKGROUND_COLOR:
1476                            nativeSetBackgroundColor(msg.arg1);
1477                            break;
1478
1479                        case DUMP_DOMTREE:
1480                            nativeDumpDomTree(msg.arg1 == 1);
1481                            break;
1482
1483                        case DUMP_RENDERTREE:
1484                            nativeDumpRenderTree(msg.arg1 == 1);
1485                            break;
1486
1487                        case DUMP_NAVTREE:
1488                            nativeDumpNavTree();
1489                            break;
1490
1491                        case DUMP_V8COUNTERS:
1492                            nativeDumpV8Counters();
1493                            break;
1494
1495                        case SET_JS_FLAGS:
1496                            nativeSetJsFlags((String)msg.obj);
1497                            break;
1498
1499                        case SAVE_WEBARCHIVE:
1500                            WebView.SaveWebArchiveMessage saveMessage =
1501                                (WebView.SaveWebArchiveMessage)msg.obj;
1502                            saveMessage.mResultFile =
1503                                saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname);
1504                            mWebView.mPrivateHandler.obtainMessage(
1505                                WebView.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
1506                            break;
1507
1508                        case GEOLOCATION_PERMISSIONS_PROVIDE:
1509                            GeolocationPermissionsData data =
1510                                    (GeolocationPermissionsData) msg.obj;
1511                            nativeGeolocationPermissionsProvide(data.mOrigin,
1512                                    data.mAllow, data.mRemember);
1513                            break;
1514
1515                        case SPLIT_PICTURE_SET:
1516                            nativeSplitContent(msg.arg1);
1517                            mWebView.mPrivateHandler.obtainMessage(
1518                                    WebView.REPLACE_BASE_CONTENT, msg.arg1, 0);
1519                            mSplitPictureIsScheduled = false;
1520                            break;
1521
1522                        case CLEAR_CONTENT:
1523                            // Clear the view so that onDraw() will draw nothing
1524                            // but white background
1525                            // (See public method WebView.clearView)
1526                            nativeClearContent();
1527                            break;
1528
1529                        case MESSAGE_RELAY:
1530                            ((Message) msg.obj).sendToTarget();
1531                            break;
1532
1533                        case POPULATE_VISITED_LINKS:
1534                            nativeProvideVisitedHistory((String[])msg.obj);
1535                            break;
1536
1537                        case VALID_NODE_BOUNDS: {
1538                            MotionUpData motionUpData = (MotionUpData) msg.obj;
1539                            if (!nativeValidNodeAndBounds(
1540                                    motionUpData.mFrame, motionUpData.mNode,
1541                                    motionUpData.mBounds)) {
1542                                nativeUpdateFrameCache();
1543                            }
1544                            Message message = mWebView.mPrivateHandler
1545                                    .obtainMessage(WebView.DO_MOTION_UP,
1546                                    motionUpData.mX, motionUpData.mY);
1547                            mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(
1548                                    message);
1549                            break;
1550                        }
1551
1552                        case HIDE_FULLSCREEN:
1553                            nativeFullScreenPluginHidden(msg.arg1);
1554                            break;
1555
1556                        case ADD_PACKAGE_NAMES:
1557                            if (BrowserFrame.sJavaBridge == null) {
1558                                throw new IllegalStateException("No WebView " +
1559                                        "has been created in this process!");
1560                            }
1561                            BrowserFrame.sJavaBridge.addPackageNames(
1562                                    (Set<String>) msg.obj);
1563                            break;
1564
1565                        case GET_TOUCH_HIGHLIGHT_RECTS:
1566                            TouchHighlightData d = (TouchHighlightData) msg.obj;
1567                            ArrayList<Rect> rects = nativeGetTouchHighlightRects
1568                                    (d.mX, d.mY, d.mSlop);
1569                            mWebView.mPrivateHandler.obtainMessage(
1570                                    WebView.SET_TOUCH_HIGHLIGHT_RECTS, rects)
1571                                    .sendToTarget();
1572                            break;
1573
1574                        case REMOVE_TOUCH_HIGHLIGHT_RECTS:
1575                            mWebView.mPrivateHandler.obtainMessage(
1576                                    WebView.SET_TOUCH_HIGHLIGHT_RECTS, null)
1577                                    .sendToTarget();
1578                            break;
1579
1580                        case USE_MOCK_DEVICE_ORIENTATION:
1581                            useMockDeviceOrientation();
1582                            break;
1583
1584                        case AUTOFILL_FORM:
1585                            nativeAutoFillForm(msg.arg1);
1586                            mWebView.mPrivateHandler.obtainMessage(WebView.AUTOFILL_COMPLETE, null)
1587                                    .sendToTarget();
1588                            break;
1589
1590                        case EXECUTE_JS:
1591                            if (msg.obj instanceof String) {
1592                                if (DebugFlags.WEB_VIEW_CORE) {
1593                                    Log.d(LOGTAG, "Executing JS : " + msg.obj);
1594                                }
1595                                mBrowserFrame.stringByEvaluatingJavaScriptFromString((String) msg.obj);
1596                            }
1597                            break;
1598                    }
1599                }
1600            };
1601            // Take all queued messages and resend them to the new handler.
1602            synchronized (this) {
1603                int size = mMessages.size();
1604                for (int i = 0; i < size; i++) {
1605                    mHandler.sendMessage(mMessages.get(i));
1606                }
1607                mMessages = null;
1608            }
1609        }
1610
1611        /**
1612         * Send a message internally to the queue or to the handler
1613         */
1614        private synchronized void sendMessage(Message msg) {
1615            if (mBlockMessages) {
1616                return;
1617            }
1618            if (mMessages != null) {
1619                mMessages.add(msg);
1620            } else {
1621                mHandler.sendMessage(msg);
1622            }
1623        }
1624
1625        private synchronized void removeMessages(int what) {
1626            if (mBlockMessages) {
1627                return;
1628            }
1629            if (what == EventHub.WEBKIT_DRAW) {
1630                mDrawIsScheduled = false;
1631            }
1632            if (mMessages != null) {
1633                Log.w(LOGTAG, "Not supported in this case.");
1634            } else {
1635                mHandler.removeMessages(what);
1636            }
1637        }
1638
1639        private synchronized boolean hasMessages(int what) {
1640            if (mBlockMessages) {
1641                return false;
1642            }
1643            if (mMessages != null) {
1644                Log.w(LOGTAG, "hasMessages() is not supported in this case.");
1645                return false;
1646            } else {
1647                return mHandler.hasMessages(what);
1648            }
1649        }
1650
1651        private synchronized void sendMessageDelayed(Message msg, long delay) {
1652            if (mBlockMessages) {
1653                return;
1654            }
1655            mHandler.sendMessageDelayed(msg, delay);
1656        }
1657
1658        /**
1659         * Send a message internally to the front of the queue.
1660         */
1661        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1662            if (mBlockMessages) {
1663                return;
1664            }
1665            if (mMessages != null) {
1666                mMessages.add(0, msg);
1667            } else {
1668                mHandler.sendMessageAtFrontOfQueue(msg);
1669            }
1670        }
1671
1672        /**
1673         * Remove all the messages.
1674         */
1675        private synchronized void removeMessages() {
1676            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1677            mDrawIsScheduled = false;
1678            mSplitPictureIsScheduled = false;
1679            if (mMessages != null) {
1680                mMessages.clear();
1681            } else {
1682                mHandler.removeCallbacksAndMessages(null);
1683            }
1684        }
1685
1686        /**
1687         * Block sending messages to the EventHub.
1688         */
1689        private synchronized void blockMessages() {
1690            mBlockMessages = true;
1691        }
1692    }
1693
1694    //-------------------------------------------------------------------------
1695    // Methods called by host activity (in the same thread)
1696    //-------------------------------------------------------------------------
1697
1698    void stopLoading() {
1699        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1700        if (mBrowserFrame != null) {
1701            mBrowserFrame.stopLoading();
1702        }
1703    }
1704
1705    //-------------------------------------------------------------------------
1706    // Methods called by WebView
1707    // If it refers to local variable, it needs synchronized().
1708    // If it needs WebCore, it has to send message.
1709    //-------------------------------------------------------------------------
1710
1711    /**
1712     * @hide
1713     */
1714    public void sendMessage(Message msg) {
1715        mEventHub.sendMessage(msg);
1716    }
1717
1718    void sendMessage(int what) {
1719        mEventHub.sendMessage(Message.obtain(null, what));
1720    }
1721
1722    void sendMessage(int what, Object obj) {
1723        mEventHub.sendMessage(Message.obtain(null, what, obj));
1724    }
1725
1726    void sendMessage(int what, int arg1) {
1727        // just ignore the second argument (make it 0)
1728        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1729    }
1730
1731    void sendMessage(int what, int arg1, int arg2) {
1732        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1733    }
1734
1735    void sendMessage(int what, int arg1, Object obj) {
1736        // just ignore the second argument (make it 0)
1737        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1738    }
1739
1740    void sendMessage(int what, int arg1, int arg2, Object obj) {
1741        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1742    }
1743
1744    void sendMessageAtFrontOfQueue(int what, Object obj) {
1745        mEventHub.sendMessageAtFrontOfQueue(Message.obtain(
1746                null, what, obj));
1747    }
1748
1749    void sendMessageDelayed(int what, Object obj, long delay) {
1750        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1751    }
1752
1753    void removeMessages(int what) {
1754        mEventHub.removeMessages(what);
1755    }
1756
1757    void removeMessages() {
1758        mEventHub.removeMessages();
1759    }
1760
1761    /**
1762     * Removes pending messages and trigger a DESTROY message to send to
1763     * WebCore.
1764     * Called from UI thread.
1765     */
1766    void destroy() {
1767        // We don't want anyone to post a message between removing pending
1768        // messages and sending the destroy message.
1769        synchronized (mEventHub) {
1770            // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
1771            // be preserved even the WebView is destroyed.
1772            // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
1773            boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
1774            boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
1775            mEventHub.removeMessages();
1776            mEventHub.sendMessageAtFrontOfQueue(
1777                    Message.obtain(null, EventHub.DESTROY));
1778            if (hasPause) {
1779                mEventHub.sendMessageAtFrontOfQueue(
1780                        Message.obtain(null, EventHub.PAUSE_TIMERS));
1781            }
1782            if (hasResume) {
1783                mEventHub.sendMessageAtFrontOfQueue(
1784                        Message.obtain(null, EventHub.RESUME_TIMERS));
1785            }
1786            mEventHub.blockMessages();
1787        }
1788    }
1789
1790    //-------------------------------------------------------------------------
1791    // WebViewCore private methods
1792    //-------------------------------------------------------------------------
1793
1794    private void clearCache(boolean includeDiskFiles) {
1795        mBrowserFrame.clearCache();
1796        if (includeDiskFiles) {
1797            CacheManager.removeAllCacheFiles();
1798        }
1799    }
1800
1801    private void loadUrl(String url, Map<String, String> extraHeaders) {
1802        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1803        mBrowserFrame.loadUrl(url, extraHeaders);
1804    }
1805
1806    private String saveWebArchive(String filename, boolean autoname) {
1807        if (DebugFlags.WEB_VIEW_CORE) {
1808            Log.v(LOGTAG, " CORE saveWebArchive " + filename + " " + autoname);
1809        }
1810        return mBrowserFrame.saveWebArchive(filename, autoname);
1811    }
1812
1813    private void key(KeyEvent evt, boolean isDown) {
1814        if (DebugFlags.WEB_VIEW_CORE) {
1815            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1816                    + evt);
1817        }
1818        int keyCode = evt.getKeyCode();
1819        int unicodeChar = evt.getUnicodeChar();
1820
1821        if (keyCode == KeyEvent.KEYCODE_UNKNOWN && evt.getCharacters() != null
1822                && evt.getCharacters().length() > 0) {
1823            // we should only receive individual complex characters
1824            unicodeChar = evt.getCharacters().codePointAt(0);
1825        }
1826
1827        if (!nativeKey(keyCode, unicodeChar, evt.getRepeatCount(), evt.isShiftPressed(),
1828                evt.isAltPressed(), evt.isSymPressed(),
1829                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1830            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1831                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1832                if (DebugFlags.WEB_VIEW_CORE) {
1833                    Log.v(LOGTAG, "key: arrow unused by page: " + keyCode);
1834                }
1835                if (mWebView != null && evt.isDown()) {
1836                    Message.obtain(mWebView.mPrivateHandler,
1837                            WebView.UNHANDLED_NAV_KEY, keyCode,
1838                            0).sendToTarget();
1839                }
1840                return;
1841            }
1842            // bubble up the event handling
1843            // but do not bubble up the ENTER key, which would open the search
1844            // bar without any text.
1845            mCallbackProxy.onUnhandledKeyEvent(evt);
1846        }
1847    }
1848
1849    // These values are used to avoid requesting a layout based on old values
1850    private int mCurrentViewWidth = 0;
1851    private int mCurrentViewHeight = 0;
1852    private float mCurrentViewScale = 1.0f;
1853
1854    // notify webkit that our virtual view size changed size (after inv-zoom)
1855    private void viewSizeChanged(WebView.ViewSizeData data) {
1856        int w = data.mWidth;
1857        int h = data.mHeight;
1858        int textwrapWidth = data.mTextWrapWidth;
1859        float scale = data.mScale;
1860        if (DebugFlags.WEB_VIEW_CORE) {
1861            Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
1862                    + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
1863        }
1864        if (w == 0) {
1865            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1866            return;
1867        }
1868        int width = calculateWindowWidth(w);
1869        int height = h;
1870        if (width != w) {
1871            float heightWidthRatio = data.mHeightWidthRatio;
1872            float ratio = (heightWidthRatio > 0) ? heightWidthRatio : (float) h / w;
1873            height = Math.round(ratio * width);
1874        }
1875        nativeSetSize(width, height, textwrapWidth, scale, w,
1876                data.mActualViewHeight > 0 ? data.mActualViewHeight : h,
1877                data.mAnchorX, data.mAnchorY, data.mIgnoreHeight);
1878        // Remember the current width and height
1879        boolean needInvalidate = (mCurrentViewWidth == 0);
1880        mCurrentViewWidth = w;
1881        mCurrentViewHeight = h;
1882        mCurrentViewScale = scale;
1883        if (needInvalidate) {
1884            // ensure {@link #webkitDraw} is called as we were blocking in
1885            // {@link #contentDraw} when mCurrentViewWidth is 0
1886            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1887            contentDraw();
1888        }
1889        mEventHub.sendMessage(Message.obtain(null,
1890                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1891    }
1892
1893    // Calculate width to be used in webkit window.
1894    private int calculateWindowWidth(int viewWidth) {
1895        int width = viewWidth;
1896        if (mSettings.getUseWideViewPort()) {
1897            if (mViewportWidth == -1) {
1898                // Fixed viewport width.
1899                width = WebView.DEFAULT_VIEWPORT_WIDTH;
1900            } else if (mViewportWidth > 0) {
1901                // Use website specified or desired fixed viewport width.
1902                width = mViewportWidth;
1903            } else {
1904                // For mobile web site.
1905                width = viewWidth;
1906            }
1907        }
1908        return width;
1909    }
1910
1911    private void sendUpdateTextEntry() {
1912        if (mWebView != null) {
1913            Message.obtain(mWebView.mPrivateHandler,
1914                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1915        }
1916    }
1917
1918    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1919    // callbacks. Computes the sum of database quota for all origins.
1920    private long getUsedQuota() {
1921        WebStorage webStorage = WebStorage.getInstance();
1922        Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
1923
1924        if (origins == null) {
1925            return 0;
1926        }
1927        long usedQuota = 0;
1928        for (WebStorage.Origin website : origins) {
1929            usedQuota += website.getQuota();
1930        }
1931        return usedQuota;
1932    }
1933
1934    // called from UI thread
1935    void splitContent(int content) {
1936        if (!mSplitPictureIsScheduled) {
1937            mSplitPictureIsScheduled = true;
1938            sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
1939        }
1940    }
1941
1942    // Used to avoid posting more than one draw message.
1943    private boolean mDrawIsScheduled;
1944    private boolean mDrawLayersIsScheduled;
1945
1946    // Used to avoid posting more than one split picture message.
1947    private boolean mSplitPictureIsScheduled;
1948
1949    // Used to suspend drawing.
1950    private boolean mDrawIsPaused;
1951
1952    // mInitialViewState is set by didFirstLayout() and then reset in the
1953    // next webkitDraw after passing the state to the UI thread.
1954    private ViewState mInitialViewState = null;
1955
1956    static class ViewState {
1957        float mMinScale;
1958        float mMaxScale;
1959        float mViewScale;
1960        float mTextWrapScale;
1961        float mDefaultScale;
1962        int mScrollX;
1963        int mScrollY;
1964        boolean mMobileSite;
1965        boolean mIsRestored;
1966    }
1967
1968    static class DrawData {
1969        DrawData() {
1970            mBaseLayer = 0;
1971            mInvalRegion = new Region();
1972            mContentSize = new Point();
1973        }
1974        int mBaseLayer;
1975        Region mInvalRegion;
1976        // view size that was used by webkit during the most recent layout
1977        Point mViewSize;
1978        Point mContentSize;
1979        int mMinPrefWidth;
1980        // only non-null if it is for the first picture set after the first layout
1981        ViewState mViewState;
1982        boolean mFocusSizeChanged;
1983    }
1984
1985    // Only update the layers' content, not the base surface
1986    // PictureSet.
1987    private void webkitDrawLayers() {
1988        mDrawLayersIsScheduled = false;
1989        if (mDrawIsScheduled) {
1990            removeMessages(EventHub.WEBKIT_DRAW);
1991            webkitDraw();
1992            return;
1993        }
1994        DrawData draw = new DrawData();
1995        draw.mBaseLayer = nativeUpdateLayers(draw.mInvalRegion);
1996        webkitDraw(draw);
1997    }
1998
1999    private void webkitDraw() {
2000        mDrawIsScheduled = false;
2001        DrawData draw = new DrawData();
2002        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
2003        draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mContentSize);
2004        if (draw.mBaseLayer == 0) {
2005            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message");
2006            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
2007            return;
2008        }
2009        webkitDraw(draw);
2010    }
2011
2012    private void webkitDraw(DrawData draw) {
2013        if (mWebView != null) {
2014            draw.mFocusSizeChanged = nativeFocusBoundsChanged();
2015            draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight);
2016            if (mSettings.getUseWideViewPort()) {
2017                draw.mMinPrefWidth = Math.max(
2018                        mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH
2019                                : (mViewportWidth == 0 ? mCurrentViewWidth
2020                                        : mViewportWidth),
2021                        nativeGetContentMinPrefWidth());
2022            }
2023            if (mInitialViewState != null) {
2024                draw.mViewState = mInitialViewState;
2025                mInitialViewState = null;
2026            }
2027            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
2028            Message.obtain(mWebView.mPrivateHandler,
2029                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
2030        }
2031    }
2032
2033    static void reducePriority() {
2034        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
2035        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
2036        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
2037        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
2038                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
2039    }
2040
2041    static void resumePriority() {
2042        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
2043        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
2044        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
2045        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
2046                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
2047    }
2048
2049    static void sendStaticMessage(int messageType, Object argument) {
2050        if (sWebCoreHandler == null)
2051            return;
2052
2053        sWebCoreHandler.sendMessage(sWebCoreHandler.obtainMessage(messageType, argument));
2054    }
2055
2056    static void pauseUpdatePicture(WebViewCore core) {
2057        // Note: there is one possible failure mode. If pauseUpdatePicture() is
2058        // called from UI thread while WEBKIT_DRAW is just pulled out of the
2059        // queue in WebCore thread to be executed. Then update won't be blocked.
2060        if (core != null) {
2061            if (!core.getSettings().enableSmoothTransition()) return;
2062
2063            synchronized (core) {
2064                core.mDrawIsPaused = true;
2065                if (core.mDrawIsScheduled) {
2066                    core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
2067                }
2068            }
2069        }
2070
2071    }
2072
2073    static void resumeUpdatePicture(WebViewCore core) {
2074        if (core != null) {
2075            // if mDrawIsPaused is true, ignore the setting, continue to resume
2076            if (!core.mDrawIsPaused
2077                    && !core.getSettings().enableSmoothTransition()) return;
2078
2079            synchronized (core) {
2080                core.mDrawIsPaused = false;
2081                // always redraw on resume to reenable gif animations
2082                core.mDrawIsScheduled = false;
2083                core.nativeContentInvalidateAll();
2084                core.contentDraw();
2085            }
2086        }
2087    }
2088
2089    //////////////////////////////////////////////////////////////////////////
2090
2091    private void restoreState(int index) {
2092        WebBackForwardList list = mCallbackProxy.getBackForwardList();
2093        int size = list.getSize();
2094        for (int i = 0; i < size; i++) {
2095            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
2096        }
2097        mBrowserFrame.mLoadInitFromJava = true;
2098        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
2099        mBrowserFrame.mLoadInitFromJava = false;
2100    }
2101
2102    //-------------------------------------------------------------------------
2103    // Implement abstract methods in WebViewCore, native WebKit callback part
2104    //-------------------------------------------------------------------------
2105
2106    // called from JNI or WebView thread
2107    /* package */ void contentDraw() {
2108        // don't update the Picture until we have an initial width and finish
2109        // the first layout
2110        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
2111            return;
2112        }
2113        // only fire an event if this is our first request
2114        synchronized (this) {
2115            if (mDrawIsScheduled) return;
2116            mDrawIsScheduled = true;
2117            if (mDrawIsPaused) return;
2118            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
2119        }
2120    }
2121
2122    // called from JNI
2123    void layersDraw() {
2124        synchronized (this) {
2125            if (mDrawLayersIsScheduled) return;
2126            mDrawLayersIsScheduled = true;
2127            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW_LAYERS));
2128        }
2129    }
2130
2131    // called by JNI
2132    private void contentScrollTo(int x, int y, boolean animate,
2133            boolean onlyIfImeIsShowing) {
2134        if (!mBrowserFrame.firstLayoutDone()) {
2135            /*
2136             * WebKit restore state will be called before didFirstLayout(),
2137             * remember the position as it has to be applied after restoring
2138             * zoom factor which is controlled by screenWidth.
2139             */
2140            mRestoredX = x;
2141            mRestoredY = y;
2142            return;
2143        }
2144        if (mWebView != null) {
2145            Message msg = Message.obtain(mWebView.mPrivateHandler,
2146                    WebView.SCROLL_TO_MSG_ID, animate ? 1 : 0,
2147                    onlyIfImeIsShowing ? 1 : 0, new Point(x, y));
2148            if (mDrawIsScheduled) {
2149                mEventHub.sendMessage(Message.obtain(null,
2150                        EventHub.MESSAGE_RELAY, msg));
2151            } else {
2152                msg.sendToTarget();
2153            }
2154        }
2155    }
2156
2157    // called by JNI
2158    private void sendNotifyProgressFinished() {
2159        sendUpdateTextEntry();
2160        if (!JniUtil.useChromiumHttpStack()) {
2161            // as CacheManager can behave based on database transaction, we need to
2162            // call tick() to trigger endTransaction
2163            WebViewWorker.getHandler().removeMessages(
2164                    WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
2165            WebViewWorker.getHandler().sendEmptyMessage(
2166                    WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
2167        }
2168        contentDraw();
2169    }
2170
2171    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
2172        be scaled before they can be used by the view system, which happens
2173        in WebView since it (and its thread) know the current scale factor.
2174     */
2175    private void sendViewInvalidate(int left, int top, int right, int bottom) {
2176        if (mWebView != null) {
2177            Message.obtain(mWebView.mPrivateHandler,
2178                           WebView.INVAL_RECT_MSG_ID,
2179                           new Rect(left, top, right, bottom)).sendToTarget();
2180        }
2181    }
2182
2183    private static boolean mRepaintScheduled = false;
2184
2185    /*
2186     * Called by the WebView thread
2187     */
2188    /* package */ void signalRepaintDone() {
2189        mRepaintScheduled = false;
2190    }
2191
2192    /* package */ WebView getWebView() {
2193        return mWebView;
2194    }
2195
2196    private native void setViewportSettingsFromNative();
2197
2198    // called by JNI
2199    private void didFirstLayout(boolean standardLoad) {
2200        if (DebugFlags.WEB_VIEW_CORE) {
2201            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
2202        }
2203
2204        mBrowserFrame.didFirstLayout();
2205
2206        if (mWebView == null) return;
2207
2208        boolean updateViewState = standardLoad || mRestoredScale > 0;
2209        setupViewport(updateViewState);
2210        // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will
2211        // be called after the WebView updates its state. If updateRestoreState
2212        // is false, start to draw now as it is ready.
2213        if (!updateViewState) {
2214            mWebView.mViewManager.postReadyToDrawAll();
2215        }
2216
2217        // remove the touch highlight when moving to a new page
2218        if (getSettings().supportTouchOnly()) {
2219            mEventHub.sendMessage(Message.obtain(null,
2220                    EventHub.REMOVE_TOUCH_HIGHLIGHT_RECTS));
2221        }
2222
2223        // reset the scroll position, the restored offset and scales
2224        mRestoredX = mRestoredY = 0;
2225        mRestoredScale = mRestoredTextWrapScale = 0;
2226    }
2227
2228    // called by JNI
2229    private void updateViewport() {
2230        // if updateViewport is called before first layout, wait until first
2231        // layout to update the viewport. In the rare case, this is called after
2232        // first layout, force an update as we have just parsed the viewport
2233        // meta tag.
2234        if (mBrowserFrame.firstLayoutDone()) {
2235            setupViewport(true);
2236        }
2237    }
2238
2239    private void setupViewport(boolean updateViewState) {
2240        // set the viewport settings from WebKit
2241        setViewportSettingsFromNative();
2242
2243        if (mSettings.forceUserScalable()) {
2244            mViewportUserScalable = true;
2245            if (mViewportInitialScale > 0) {
2246                if (mViewportMinimumScale > 0) {
2247                    mViewportMinimumScale = Math.min(mViewportMinimumScale,
2248                            mViewportInitialScale / 2);
2249                }
2250                if (mViewportMaximumScale > 0) {
2251                    mViewportMaximumScale = Math.max(mViewportMaximumScale,
2252                            mViewportInitialScale * 2);
2253                }
2254            } else {
2255                if (mViewportMinimumScale > 0) {
2256                    mViewportMinimumScale = Math.min(mViewportMinimumScale, 50);
2257                }
2258                if (mViewportMaximumScale > 0) {
2259                    mViewportMaximumScale = Math.max(mViewportMaximumScale, 200);
2260                }
2261            }
2262        }
2263
2264        // adjust the default scale to match the densityDpi
2265        float adjust = 1.0f;
2266        if (mViewportDensityDpi == -1) {
2267            // convert default zoom scale to a integer (percentage) to avoid any
2268            // issues with floating point comparisons
2269            if (mWebView != null && (int)(mWebView.getDefaultZoomScale() * 100) != 100) {
2270                adjust = mWebView.getDefaultZoomScale();
2271            }
2272        } else if (mViewportDensityDpi > 0) {
2273            adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
2274                    / mViewportDensityDpi;
2275        }
2276        int defaultScale = (int) (adjust * 100);
2277
2278        if (mViewportInitialScale > 0) {
2279            mViewportInitialScale *= adjust;
2280        }
2281        if (mViewportMinimumScale > 0) {
2282            mViewportMinimumScale *= adjust;
2283        }
2284        if (mViewportMaximumScale > 0) {
2285            mViewportMaximumScale *= adjust;
2286        }
2287
2288        // infer the values if they are not defined.
2289        if (mViewportWidth == 0) {
2290            if (mViewportInitialScale == 0) {
2291                mViewportInitialScale = defaultScale;
2292            }
2293        }
2294        if (mViewportUserScalable == false) {
2295            mViewportInitialScale = defaultScale;
2296            mViewportMinimumScale = defaultScale;
2297            mViewportMaximumScale = defaultScale;
2298        }
2299        if (mViewportMinimumScale > mViewportInitialScale
2300                && mViewportInitialScale != 0) {
2301            mViewportMinimumScale = mViewportInitialScale;
2302        }
2303        if (mViewportMaximumScale > 0
2304                && mViewportMaximumScale < mViewportInitialScale) {
2305            mViewportMaximumScale = mViewportInitialScale;
2306        }
2307        if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
2308            mViewportWidth = 0;
2309        }
2310
2311        // if mViewportWidth is 0, it means device-width, always update.
2312        if (mViewportWidth != 0 && !updateViewState) {
2313            ViewState viewState = new ViewState();
2314            viewState.mMinScale = mViewportMinimumScale / 100.0f;
2315            viewState.mMaxScale = mViewportMaximumScale / 100.0f;
2316            viewState.mDefaultScale = adjust;
2317            // as mViewportWidth is not 0, it is not mobile site.
2318            viewState.mMobileSite = false;
2319            // for non-mobile site, we don't need minPrefWidth, set it as 0
2320            viewState.mScrollX = 0;
2321            Message.obtain(mWebView.mPrivateHandler,
2322                    WebView.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
2323            return;
2324        }
2325
2326        // now notify webview
2327        // webViewWidth refers to the width in the view system
2328        int webViewWidth;
2329        // viewportWidth refers to the width in the document system
2330        int viewportWidth = mCurrentViewWidth;
2331        if (viewportWidth == 0) {
2332            // this may happen when WebView just starts. This is not perfect as
2333            // we call WebView method from WebCore thread. But not perfect
2334            // reference is better than no reference.
2335            webViewWidth = mWebView.getViewWidth();
2336            viewportWidth = (int) (webViewWidth / adjust);
2337            if (viewportWidth == 0) {
2338                Log.w(LOGTAG, "Can't get the viewWidth after the first layout");
2339            }
2340        } else {
2341            webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
2342        }
2343        mInitialViewState = new ViewState();
2344        mInitialViewState.mMinScale = mViewportMinimumScale / 100.0f;
2345        mInitialViewState.mMaxScale = mViewportMaximumScale / 100.0f;
2346        mInitialViewState.mDefaultScale = adjust;
2347        mInitialViewState.mScrollX = mRestoredX;
2348        mInitialViewState.mScrollY = mRestoredY;
2349        mInitialViewState.mMobileSite = (0 == mViewportWidth);
2350        if (mRestoredScale > 0) {
2351            mInitialViewState.mIsRestored = true;
2352            mInitialViewState.mViewScale = mRestoredScale;
2353            if (mRestoredTextWrapScale > 0) {
2354                mInitialViewState.mTextWrapScale = mRestoredTextWrapScale;
2355            } else {
2356                mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale;
2357            }
2358        } else {
2359            if (mViewportInitialScale > 0) {
2360                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
2361                        mViewportInitialScale / 100.0f;
2362            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth &&
2363                !mWebView.getSettings().getUseFixedViewport()) {
2364                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
2365                        (float) webViewWidth / mViewportWidth;
2366            } else {
2367                mInitialViewState.mTextWrapScale = adjust;
2368                // 0 will trigger WebView to turn on zoom overview mode
2369                mInitialViewState.mViewScale = 0;
2370            }
2371        }
2372
2373        if (mWebView.mHeightCanMeasure) {
2374            // Trick to ensure that the Picture has the exact height for the
2375            // content by forcing to layout with 0 height after the page is
2376            // ready, which is indicated by didFirstLayout. This is essential to
2377            // get rid of the white space in the GMail which uses WebView for
2378            // message view.
2379            mWebView.mLastHeightSent = 0;
2380            // Send a negative scale to indicate that WebCore should reuse
2381            // the current scale
2382            WebView.ViewSizeData data = new WebView.ViewSizeData();
2383            data.mWidth = mWebView.mLastWidthSent;
2384            data.mHeight = 0;
2385            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
2386            // true. It is safe to use mWidth for mTextWrapWidth.
2387            data.mTextWrapWidth = data.mWidth;
2388            data.mScale = -1.0f;
2389            data.mIgnoreHeight = false;
2390            data.mAnchorX = data.mAnchorY = 0;
2391            // send VIEW_SIZE_CHANGED to the front of the queue so that we can
2392            // avoid pushing the wrong picture to the WebView side. If there is
2393            // a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
2394            // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED
2395            // in the queue, as mLastHeightSent has been updated here, we may
2396            // miss the requestLayout in WebView side after the new picture.
2397            mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2398            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2399                    EventHub.VIEW_SIZE_CHANGED, data));
2400        } else if (mSettings.getUseWideViewPort()) {
2401            if (viewportWidth == 0) {
2402                // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
2403                // to WebViewCore
2404                mWebView.mLastWidthSent = 0;
2405            } else {
2406                WebView.ViewSizeData data = new WebView.ViewSizeData();
2407                // mViewScale as 0 means it is in zoom overview mode. So we don't
2408                // know the exact scale. If mRestoredScale is non-zero, use it;
2409                // otherwise just use mTextWrapScale as the initial scale.
2410                float tentativeScale = mInitialViewState.mViewScale;
2411                if (tentativeScale == 0) {
2412                    // The following tries to figure out more correct view scale
2413                    // and text wrap scale to be sent to webkit, by using some
2414                    // knowledge from web settings and zoom manager.
2415
2416                    // Calculated window width will be used to guess the scale
2417                    // in zoom overview mode.
2418                    tentativeScale = mInitialViewState.mTextWrapScale;
2419                    int tentativeViewWidth = Math.round(webViewWidth / tentativeScale);
2420                    int windowWidth = calculateWindowWidth(tentativeViewWidth);
2421                    // In viewport setup time, since no content width is known, we assume
2422                    // the windowWidth will be the content width, to get a more likely
2423                    // zoom overview scale.
2424                    data.mScale = (float) webViewWidth / windowWidth;
2425                    if (!mSettings.getLoadWithOverviewMode()) {
2426                        // If user choose non-overview mode.
2427                        data.mScale = Math.max(data.mScale, tentativeScale);
2428                    }
2429                    if (mSettings.isNarrowColumnLayout()) {
2430                        // In case of automatic text reflow in fixed view port mode.
2431                        mInitialViewState.mTextWrapScale =
2432                                ZoomManager.computeReadingLevelScale(data.mScale);
2433                    }
2434                } else {
2435                    // Scale is given such as when page is restored, use it.
2436                    data.mScale = tentativeScale;
2437                }
2438                if (DebugFlags.WEB_VIEW_CORE) {
2439                    Log.v(LOGTAG, "setupViewport"
2440                             + " mRestoredScale=" + mRestoredScale
2441                             + " mViewScale=" + mInitialViewState.mViewScale
2442                             + " mTextWrapScale=" + mInitialViewState.mTextWrapScale
2443                             + " data.mScale= " + data.mScale
2444                             );
2445                }
2446                data.mWidth = Math.round(webViewWidth / data.mScale);
2447                // We may get a call here when mCurrentViewHeight == 0 if webcore completes the
2448                // first layout before we sync our webview dimensions to it. In that case, we
2449                // request the real height of the webview. This is not a perfect solution as we
2450                // are calling a WebView method from the WebCore thread. But this is preferable
2451                // to syncing an incorrect height.
2452                data.mHeight = mCurrentViewHeight == 0 ?
2453                        Math.round(mWebView.getViewHeight() / data.mScale)
2454                        : Math.round((float) mCurrentViewHeight * data.mWidth / viewportWidth);
2455                data.mTextWrapWidth = Math.round(webViewWidth
2456                        / mInitialViewState.mTextWrapScale);
2457                data.mIgnoreHeight = false;
2458                data.mAnchorX = data.mAnchorY = 0;
2459                // send VIEW_SIZE_CHANGED to the front of the queue so that we
2460                // can avoid pushing the wrong picture to the WebView side.
2461                mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2462                // Let webkit know the scale and inner width/height immediately
2463                // in viewport setup time to avoid wrong information.
2464                viewSizeChanged(data);
2465            }
2466        }
2467    }
2468
2469    // called by JNI
2470    private void restoreScale(float scale, float textWrapScale) {
2471        if (mBrowserFrame.firstLayoutDone() == false) {
2472            mRestoredScale = scale;
2473            if (mSettings.getUseWideViewPort()) {
2474                mRestoredTextWrapScale = textWrapScale;
2475            }
2476        }
2477    }
2478
2479    // called by JNI
2480    private void needTouchEvents(boolean need) {
2481        if (mWebView != null) {
2482            Message.obtain(mWebView.mPrivateHandler,
2483                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
2484                    .sendToTarget();
2485        }
2486    }
2487
2488    // called by JNI
2489    private void updateTextfield(int ptr, boolean changeToPassword,
2490            String text, int textGeneration) {
2491        if (mWebView != null) {
2492            Message msg = Message.obtain(mWebView.mPrivateHandler,
2493                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
2494                    textGeneration, text);
2495            msg.getData().putBoolean("password", changeToPassword);
2496            msg.sendToTarget();
2497        }
2498    }
2499
2500    // called by JNI
2501    private void updateTextSelection(int pointer, int start, int end,
2502            int textGeneration) {
2503        if (mWebView != null) {
2504            Message.obtain(mWebView.mPrivateHandler,
2505                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
2506                new TextSelectionData(start, end)).sendToTarget();
2507        }
2508    }
2509
2510    // called by JNI
2511    private void clearTextEntry() {
2512        if (mWebView == null) return;
2513        Message.obtain(mWebView.mPrivateHandler,
2514                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
2515    }
2516
2517    // called by JNI
2518    private void sendFindAgain() {
2519        if (mWebView == null) return;
2520        Message.obtain(mWebView.mPrivateHandler,
2521                WebView.FIND_AGAIN).sendToTarget();
2522    }
2523
2524    private native void nativeUpdateFrameCacheIfLoading();
2525    private native void nativeRevealSelection();
2526    private native String nativeRequestLabel(int framePtr, int nodePtr);
2527    /**
2528     * Scroll the focused textfield to (xPercent, y) in document space
2529     */
2530    private native void nativeScrollFocusedTextInput(float xPercent, int y);
2531
2532    // these must be in document space (i.e. not scaled/zoomed).
2533    private native void nativeSetScrollOffset(int gen, boolean sendScrollEvent, int dx, int dy);
2534
2535    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
2536
2537    // called by JNI
2538    private void requestListBox(String[] array, int[] enabledArray,
2539            int[] selectedArray) {
2540        if (mWebView != null) {
2541            mWebView.requestListBox(array, enabledArray, selectedArray);
2542        }
2543    }
2544
2545    // called by JNI
2546    private void requestListBox(String[] array, int[] enabledArray,
2547            int selection) {
2548        if (mWebView != null) {
2549            mWebView.requestListBox(array, enabledArray, selection);
2550        }
2551
2552    }
2553
2554    // called by JNI
2555    private void requestKeyboardWithSelection(int pointer, int selStart,
2556            int selEnd, int textGeneration) {
2557        if (mWebView != null) {
2558            Message.obtain(mWebView.mPrivateHandler,
2559                    WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
2560                    textGeneration, new TextSelectionData(selStart, selEnd))
2561                    .sendToTarget();
2562        }
2563    }
2564
2565    // called by JNI
2566    private void requestKeyboard(boolean showKeyboard) {
2567        if (mWebView != null) {
2568            Message.obtain(mWebView.mPrivateHandler,
2569                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
2570                    .sendToTarget();
2571        }
2572    }
2573
2574    private void setWebTextViewAutoFillable(int queryId, String preview) {
2575        if (mWebView != null) {
2576            Message.obtain(mWebView.mPrivateHandler, WebView.SET_AUTOFILLABLE,
2577                    new AutoFillData(queryId, preview))
2578                    .sendToTarget();
2579        }
2580    }
2581
2582    // called by JNI
2583    private Context getContext() {
2584        return mContext;
2585    }
2586
2587    // called by JNI
2588    private void keepScreenOn(boolean screenOn) {
2589        if (mWebView != null) {
2590            Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SCREEN_ON);
2591            message.arg1 = screenOn ? 1 : 0;
2592            message.sendToTarget();
2593        }
2594    }
2595
2596    // called by JNI
2597    private Class<?> getPluginClass(String libName, String clsName) {
2598
2599        if (mWebView == null) {
2600            return null;
2601        }
2602
2603        PluginManager pluginManager = PluginManager.getInstance(null);
2604
2605        String pkgName = pluginManager.getPluginsAPKName(libName);
2606        if (pkgName == null) {
2607            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2608            return null;
2609        }
2610
2611        try {
2612            return pluginManager.getPluginClass(pkgName, clsName);
2613        } catch (NameNotFoundException e) {
2614            Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")");
2615        } catch (ClassNotFoundException e) {
2616            Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
2617                    ") in the apk (" + pkgName + ")");
2618        }
2619
2620        return null;
2621    }
2622
2623    // called by JNI. PluginWidget function to launch a full-screen view using a
2624    // View object provided by the plugin class.
2625    private void showFullScreenPlugin(ViewManager.ChildView childView, int npp) {
2626        if (mWebView == null) {
2627            return;
2628        }
2629
2630        Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN);
2631        message.obj = childView.mView;
2632        message.arg1 = npp;
2633        message.sendToTarget();
2634    }
2635
2636    // called by JNI
2637    private void hideFullScreenPlugin() {
2638        if (mWebView == null) {
2639            return;
2640        }
2641        mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
2642                .sendToTarget();
2643    }
2644
2645    private ViewManager.ChildView createSurface(View pluginView) {
2646        if (mWebView == null) {
2647            return null;
2648        }
2649
2650        if (pluginView == null) {
2651            Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy");
2652            return null;
2653        }
2654
2655        // ensures the view system knows the view can redraw itself
2656        pluginView.setWillNotDraw(false);
2657
2658        if(pluginView instanceof SurfaceView)
2659            ((SurfaceView)pluginView).setZOrderOnTop(true);
2660
2661        ViewManager.ChildView view = mWebView.mViewManager.createView();
2662        view.mView = pluginView;
2663        return view;
2664    }
2665
2666    // called by JNI.  PluginWidget functions for creating an embedded View for
2667    // the surface drawing model.
2668    private ViewManager.ChildView addSurface(View pluginView, int x, int y,
2669                                             int width, int height) {
2670        ViewManager.ChildView view = createSurface(pluginView);
2671        view.attachView(x, y, width, height);
2672        return view;
2673    }
2674
2675    private void updateSurface(ViewManager.ChildView childView, int x, int y,
2676            int width, int height) {
2677        childView.attachView(x, y, width, height);
2678    }
2679
2680    private void destroySurface(ViewManager.ChildView childView) {
2681        childView.removeView();
2682    }
2683
2684    // called by JNI
2685    static class ShowRectData {
2686        int mLeft;
2687        int mTop;
2688        int mWidth;
2689        int mHeight;
2690        int mContentWidth;
2691        int mContentHeight;
2692        float mXPercentInDoc;
2693        float mXPercentInView;
2694        float mYPercentInDoc;
2695        float mYPercentInView;
2696    }
2697
2698    private void showRect(int left, int top, int width, int height,
2699            int contentWidth, int contentHeight, float xPercentInDoc,
2700            float xPercentInView, float yPercentInDoc, float yPercentInView) {
2701        if (mWebView != null) {
2702            ShowRectData data = new ShowRectData();
2703            data.mLeft = left;
2704            data.mTop = top;
2705            data.mWidth = width;
2706            data.mHeight = height;
2707            data.mContentWidth = contentWidth;
2708            data.mContentHeight = contentHeight;
2709            data.mXPercentInDoc = xPercentInDoc;
2710            data.mXPercentInView = xPercentInView;
2711            data.mYPercentInDoc = yPercentInDoc;
2712            data.mYPercentInView = yPercentInView;
2713            Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID,
2714                    data).sendToTarget();
2715        }
2716    }
2717
2718    // called by JNI
2719    private void centerFitRect(int x, int y, int width, int height) {
2720        if (mWebView == null) {
2721            return;
2722        }
2723        mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT,
2724                new Rect(x, y, x + width, y + height)).sendToTarget();
2725    }
2726
2727    // called by JNI
2728    private void setScrollbarModes(int hMode, int vMode) {
2729        if (mWebView == null) {
2730            return;
2731        }
2732        mWebView.mPrivateHandler.obtainMessage(WebView.SET_SCROLLBAR_MODES,
2733                hMode, vMode).sendToTarget();
2734    }
2735
2736    // called by JNI
2737    @SuppressWarnings("unused")
2738    private void selectAt(int x, int y) {
2739        if (mWebView != null) {
2740            mWebView.mPrivateHandler.obtainMessage(WebView.SELECT_AT, x, y).sendToTarget();
2741        }
2742    }
2743
2744    private void useMockDeviceOrientation() {
2745        mDeviceMotionAndOrientationManager.useMock();
2746    }
2747
2748    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
2749            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
2750        mDeviceMotionAndOrientationManager.setMockOrientation(canProvideAlpha, alpha,
2751                canProvideBeta, beta, canProvideGamma, gamma);
2752    }
2753
2754    protected DeviceMotionService getDeviceMotionService() {
2755        if (mDeviceMotionService == null) {
2756            mDeviceMotionService =
2757                    new DeviceMotionService(mDeviceMotionAndOrientationManager, mContext);
2758        }
2759        return mDeviceMotionService;
2760    }
2761
2762    protected DeviceOrientationService getDeviceOrientationService() {
2763        if (mDeviceOrientationService == null) {
2764            mDeviceOrientationService =
2765                    new DeviceOrientationService(mDeviceMotionAndOrientationManager, mContext);
2766        }
2767        return mDeviceOrientationService;
2768    }
2769
2770    private native void nativePause();
2771    private native void nativeResume();
2772    private native void nativeFreeMemory();
2773    private native void nativeFullScreenPluginHidden(int npp);
2774    private native boolean nativeValidNodeAndBounds(int frame, int node,
2775            Rect bounds);
2776
2777    private native ArrayList<Rect> nativeGetTouchHighlightRects(int x, int y,
2778            int slop);
2779
2780   private native void nativeAutoFillForm(int queryId);
2781   private native void nativeScrollLayer(int layer, Rect rect);
2782}
2783