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