WebViewCore.java revision 00d5bf7e44d44510d0828c473619c95a9ea783e3
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 */ synchronized 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                    if (mWebView == null
1073                            && msg.what != EventHub.RESUME_TIMERS
1074                            && msg.what != EventHub.PAUSE_TIMERS) {
1075                        if (DebugFlags.WEB_VIEW_CORE) {
1076                            Log.v(LOGTAG, "Rejecting message " + msg.what
1077                                    + " because we are destroyed");
1078                        }
1079                        return;
1080                    }
1081                    switch (msg.what) {
1082                        case WEBKIT_DRAW:
1083                            webkitDraw();
1084                            break;
1085
1086                        case WEBKIT_DRAW_LAYERS:
1087                            webkitDrawLayers();
1088                            break;
1089
1090                        case DESTROY:
1091                            // Time to take down the world. Cancel all pending
1092                            // loads and destroy the native view and frame.
1093                            synchronized (WebViewCore.this) {
1094                                mBrowserFrame.destroy();
1095                                mBrowserFrame = null;
1096                                mSettings.onDestroyed();
1097                                mNativeClass = 0;
1098                                mWebView = null;
1099                            }
1100                            break;
1101
1102                        case REVEAL_SELECTION:
1103                            nativeRevealSelection();
1104                            break;
1105
1106                        case REQUEST_LABEL:
1107                            if (mWebView != null) {
1108                                int nodePointer = msg.arg2;
1109                                String label = nativeRequestLabel(msg.arg1,
1110                                        nodePointer);
1111                                if (label != null && label.length() > 0) {
1112                                    Message.obtain(mWebView.mPrivateHandler,
1113                                            WebView.RETURN_LABEL, nodePointer,
1114                                            0, label).sendToTarget();
1115                                }
1116                            }
1117                            break;
1118
1119                        case UPDATE_FRAME_CACHE_IF_LOADING:
1120                            nativeUpdateFrameCacheIfLoading();
1121                            break;
1122
1123                        case SCROLL_TEXT_INPUT:
1124                            float xPercent;
1125                            if (msg.obj == null) {
1126                                xPercent = 0f;
1127                            } else {
1128                                xPercent = ((Float) msg.obj).floatValue();
1129                            }
1130                            nativeScrollFocusedTextInput(xPercent, msg.arg2);
1131                            break;
1132
1133                        case LOAD_URL: {
1134                            CookieManager.getInstance().waitForCookieOperationsToComplete();
1135                            GetUrlData param = (GetUrlData) msg.obj;
1136                            loadUrl(param.mUrl, param.mExtraHeaders);
1137                            break;
1138                        }
1139
1140                        case POST_URL: {
1141                            CookieManager.getInstance().waitForCookieOperationsToComplete();
1142                            PostUrlData param = (PostUrlData) msg.obj;
1143                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
1144                            break;
1145                        }
1146                        case LOAD_DATA:
1147                            CookieManager.getInstance().waitForCookieOperationsToComplete();
1148                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
1149                            String baseUrl = loadParams.mBaseUrl;
1150                            if (baseUrl != null) {
1151                                int i = baseUrl.indexOf(':');
1152                                if (i > 0) {
1153                                    // In 1.0, WebView.loadDataWithBaseURL() could access local
1154                                    // asset files using 'file' scheme URLs as long as the data is
1155                                    // valid. Later versions of WebKit have tightened the
1156                                    // restriction around when pages can access such local URLs.
1157                                    // To maintain compatibility with 1.0, we register the scheme of
1158                                    // the baseUrl to be considered local, as long as it is not
1159                                    // http(s)/ftp(s)/about/javascript.
1160                                    String scheme = baseUrl.substring(0, i);
1161                                    if (!scheme.startsWith("http") &&
1162                                            !scheme.startsWith("ftp") &&
1163                                            !scheme.startsWith("about") &&
1164                                            !scheme.startsWith("javascript")) {
1165                                        nativeRegisterURLSchemeAsLocal(scheme);
1166                                    }
1167                                }
1168                            }
1169                            mBrowserFrame.loadData(baseUrl,
1170                                    loadParams.mData,
1171                                    loadParams.mMimeType,
1172                                    loadParams.mEncoding,
1173                                    loadParams.mHistoryUrl);
1174                            break;
1175
1176                        case STOP_LOADING:
1177                            // If the WebCore has committed the load, but not
1178                            // finished the first layout yet, we need to set
1179                            // first layout done to trigger the interpreted side sync
1180                            // up with native side
1181                            if (mBrowserFrame.committed()
1182                                    && !mBrowserFrame.firstLayoutDone()) {
1183                                mBrowserFrame.didFirstLayout();
1184                            }
1185                            // Do this after syncing up the layout state.
1186                            stopLoading();
1187                            break;
1188
1189                        case RELOAD:
1190                            mBrowserFrame.reload(false);
1191                            break;
1192
1193                        case KEY_DOWN:
1194                            key((KeyEvent) msg.obj, true);
1195                            break;
1196
1197                        case KEY_UP:
1198                            key((KeyEvent) msg.obj, false);
1199                            break;
1200
1201                        case FAKE_CLICK:
1202                            nativeClick(msg.arg1, msg.arg2, true);
1203                            break;
1204
1205                        case CLICK:
1206                            nativeClick(msg.arg1, msg.arg2, false);
1207                            break;
1208
1209                        case VIEW_SIZE_CHANGED: {
1210                            viewSizeChanged((WebView.ViewSizeData) msg.obj);
1211                            break;
1212                        }
1213                        case SET_SCROLL_OFFSET:
1214                            // note: these are in document coordinates
1215                            // (inv-zoom)
1216                            Point pt = (Point) msg.obj;
1217                            nativeSetScrollOffset(msg.arg1, msg.arg2 == 1,
1218                                    pt.x, pt.y);
1219                            break;
1220
1221                        case SET_GLOBAL_BOUNDS:
1222                            Rect r = (Rect) msg.obj;
1223                            nativeSetGlobalBounds(r.left, r.top, r.width(),
1224                                r.height());
1225                            break;
1226
1227                        case GO_BACK_FORWARD:
1228                            // If it is a standard load and the load is not
1229                            // committed yet, we interpret BACK as RELOAD
1230                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
1231                                    (mBrowserFrame.loadType() ==
1232                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
1233                                mBrowserFrame.reload(true);
1234                            } else {
1235                                mBrowserFrame.goBackOrForward(msg.arg1);
1236                            }
1237                            break;
1238
1239                        case RESTORE_STATE:
1240                            stopLoading();
1241                            restoreState(msg.arg1);
1242                            break;
1243
1244                        case PAUSE_TIMERS:
1245                            mSavedPriority = Process.getThreadPriority(mTid);
1246                            Process.setThreadPriority(mTid,
1247                                    Process.THREAD_PRIORITY_BACKGROUND);
1248                            pauseTimers();
1249                            if (!JniUtil.useChromiumHttpStack()) {
1250                                WebViewWorker.getHandler().sendEmptyMessage(
1251                                        WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION);
1252                            }
1253                            break;
1254
1255                        case RESUME_TIMERS:
1256                            Process.setThreadPriority(mTid, mSavedPriority);
1257                            resumeTimers();
1258                            if (!JniUtil.useChromiumHttpStack()) {
1259                                WebViewWorker.getHandler().sendEmptyMessage(
1260                                        WebViewWorker.MSG_RESUME_CACHE_TRANSACTION);
1261                            }
1262                            break;
1263
1264                        case ON_PAUSE:
1265                            nativePause();
1266                            break;
1267
1268                        case ON_RESUME:
1269                            nativeResume();
1270                            break;
1271
1272                        case FREE_MEMORY:
1273                            clearCache(false);
1274                            nativeFreeMemory();
1275                            break;
1276
1277                        case SET_NETWORK_STATE:
1278                            if (BrowserFrame.sJavaBridge == null) {
1279                                throw new IllegalStateException("No WebView " +
1280                                        "has been created in this process!");
1281                            }
1282                            BrowserFrame.sJavaBridge
1283                                    .setNetworkOnLine(msg.arg1 == 1);
1284                            break;
1285
1286                        case SET_NETWORK_TYPE:
1287                            if (BrowserFrame.sJavaBridge == null) {
1288                                throw new IllegalStateException("No WebView " +
1289                                        "has been created in this process!");
1290                            }
1291                            Map<String, String> map = (Map<String, String>) msg.obj;
1292                            BrowserFrame.sJavaBridge
1293                                    .setNetworkType(map.get("type"), map.get("subtype"));
1294                            break;
1295
1296                        case CLEAR_CACHE:
1297                            clearCache(msg.arg1 == 1);
1298                            break;
1299
1300                        case CLEAR_HISTORY:
1301                            mCallbackProxy.getBackForwardList().
1302                                    close(mBrowserFrame.mNativeFrame);
1303                            break;
1304
1305                        case REPLACE_TEXT:
1306                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
1307                            nativeReplaceTextfieldText(msg.arg1, msg.arg2,
1308                                    rep.mReplace, rep.mNewStart, rep.mNewEnd,
1309                                    rep.mTextGeneration);
1310                            break;
1311
1312                        case PASS_TO_JS: {
1313                            JSKeyData jsData = (JSKeyData) msg.obj;
1314                            KeyEvent evt = jsData.mEvent;
1315                            int keyCode = evt.getKeyCode();
1316                            int keyValue = evt.getUnicodeChar();
1317                            int generation = msg.arg1;
1318                            passToJs(generation,
1319                                    jsData.mCurrentText,
1320                                    keyCode,
1321                                    keyValue,
1322                                    evt.isDown(),
1323                                    evt.isShiftPressed(), evt.isAltPressed(),
1324                                    evt.isSymPressed());
1325                            break;
1326                        }
1327
1328                        case SAVE_DOCUMENT_STATE: {
1329                            CursorData cDat = (CursorData) msg.obj;
1330                            nativeSaveDocumentState(cDat.mFrame);
1331                            break;
1332                        }
1333
1334                        case CLEAR_SSL_PREF_TABLE:
1335                            Network.getInstance(mContext)
1336                                    .clearUserSslPrefTable();
1337                            break;
1338
1339                        case TOUCH_UP:
1340                            TouchUpData touchUpData = (TouchUpData) msg.obj;
1341                            if (touchUpData.mNativeLayer != 0) {
1342                                nativeScrollLayer(touchUpData.mNativeLayer,
1343                                        touchUpData.mNativeLayerRect);
1344                            }
1345                            nativeTouchUp(touchUpData.mMoveGeneration,
1346                                    touchUpData.mFrame, touchUpData.mNode,
1347                                    touchUpData.mX, touchUpData.mY);
1348                            break;
1349
1350                        case TOUCH_EVENT: {
1351                            TouchEventData ted = (TouchEventData) msg.obj;
1352                            final int count = ted.mPoints.length;
1353                            int[] xArray = new int[count];
1354                            int[] yArray = new int[count];
1355                            for (int c = 0; c < count; c++) {
1356                                xArray[c] = ted.mPoints[c].x;
1357                                yArray[c] = ted.mPoints[c].y;
1358                            }
1359                            if (ted.mNativeLayer != 0) {
1360                                nativeScrollLayer(ted.mNativeLayer,
1361                                        ted.mNativeLayerRect);
1362                            }
1363                            ted.mNativeResult = nativeHandleTouchEvent(ted.mAction, ted.mIds,
1364                                    xArray, yArray, count, ted.mActionIndex, ted.mMetaState);
1365                            Message.obtain(
1366                                    mWebView.mPrivateHandler,
1367                                    WebView.PREVENT_TOUCH_ID,
1368                                    ted.mAction,
1369                                    ted.mNativeResult ? 1 : 0,
1370                                    ted).sendToTarget();
1371                            break;
1372                        }
1373
1374                        case SET_ACTIVE:
1375                            nativeSetFocusControllerActive(msg.arg1 == 1);
1376                            break;
1377
1378                        case ADD_JS_INTERFACE:
1379                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
1380                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
1381                                    jsData.mInterfaceName);
1382                            break;
1383
1384                        case REMOVE_JS_INTERFACE:
1385                            jsData = (JSInterfaceData) msg.obj;
1386                            mBrowserFrame.removeJavascriptInterface(
1387                                    jsData.mInterfaceName);
1388                            break;
1389
1390                        case REQUEST_EXT_REPRESENTATION:
1391                            mBrowserFrame.externalRepresentation(
1392                                    (Message) msg.obj);
1393                            break;
1394
1395                        case REQUEST_DOC_AS_TEXT:
1396                            mBrowserFrame.documentAsText((Message) msg.obj);
1397                            break;
1398
1399                        case SET_MOVE_FOCUS:
1400                            CursorData focusData = (CursorData) msg.obj;
1401                            nativeMoveFocus(focusData.mFrame, focusData.mNode);
1402                            break;
1403
1404                        case SET_MOVE_MOUSE:
1405                            CursorData cursorData = (CursorData) msg.obj;
1406                            nativeMoveMouse(cursorData.mFrame,
1407                                     cursorData.mX, cursorData.mY);
1408                            break;
1409
1410                        case SET_MOVE_MOUSE_IF_LATEST:
1411                            CursorData cData = (CursorData) msg.obj;
1412                            nativeMoveMouseIfLatest(cData.mMoveGeneration,
1413                                    cData.mFrame,
1414                                    cData.mX, cData.mY);
1415                            if (msg.arg1 == 1) {
1416                                nativeStopPaintingCaret();
1417                            }
1418                            break;
1419
1420                        case REQUEST_CURSOR_HREF: {
1421                            Message hrefMsg = (Message) msg.obj;
1422                            hrefMsg.getData().putString("url",
1423                                    nativeRetrieveHref(msg.arg1, msg.arg2));
1424                            hrefMsg.getData().putString("title",
1425                                    nativeRetrieveAnchorText(msg.arg1, msg.arg2));
1426                            hrefMsg.getData().putString("src",
1427                                    nativeRetrieveImageSource(msg.arg1, msg.arg2));
1428                            hrefMsg.sendToTarget();
1429                            break;
1430                        }
1431
1432                        case UPDATE_CACHE_AND_TEXT_ENTRY:
1433                            nativeUpdateFrameCache();
1434                            // FIXME: this should provide a minimal rectangle
1435                            if (mWebView != null) {
1436                                mWebView.postInvalidate();
1437                            }
1438                            sendUpdateTextEntry();
1439                            break;
1440
1441                        case DOC_HAS_IMAGES:
1442                            Message imageResult = (Message) msg.obj;
1443                            imageResult.arg1 =
1444                                    mBrowserFrame.documentHasImages() ? 1 : 0;
1445                            imageResult.sendToTarget();
1446                            break;
1447
1448                        case DELETE_SELECTION:
1449                            TextSelectionData deleteSelectionData
1450                                    = (TextSelectionData) msg.obj;
1451                            nativeDeleteSelection(deleteSelectionData.mStart,
1452                                    deleteSelectionData.mEnd, msg.arg1);
1453                            break;
1454
1455                        case SET_SELECTION:
1456                            nativeSetSelection(msg.arg1, msg.arg2);
1457                            break;
1458
1459                        case MODIFY_SELECTION:
1460                            String modifiedSelectionString = nativeModifySelection(msg.arg1,
1461                                    msg.arg2);
1462                            mWebView.mPrivateHandler.obtainMessage(WebView.SELECTION_STRING_CHANGED,
1463                                    modifiedSelectionString).sendToTarget();
1464                            break;
1465
1466                        case LISTBOX_CHOICES:
1467                            SparseBooleanArray choices = (SparseBooleanArray)
1468                                    msg.obj;
1469                            int choicesSize = msg.arg1;
1470                            boolean[] choicesArray = new boolean[choicesSize];
1471                            for (int c = 0; c < choicesSize; c++) {
1472                                choicesArray[c] = choices.get(c);
1473                            }
1474                            nativeSendListBoxChoices(choicesArray,
1475                                    choicesSize);
1476                            break;
1477
1478                        case SINGLE_LISTBOX_CHOICE:
1479                            nativeSendListBoxChoice(msg.arg1);
1480                            break;
1481
1482                        case SET_BACKGROUND_COLOR:
1483                            nativeSetBackgroundColor(msg.arg1);
1484                            break;
1485
1486                        case DUMP_DOMTREE:
1487                            nativeDumpDomTree(msg.arg1 == 1);
1488                            break;
1489
1490                        case DUMP_RENDERTREE:
1491                            nativeDumpRenderTree(msg.arg1 == 1);
1492                            break;
1493
1494                        case DUMP_NAVTREE:
1495                            nativeDumpNavTree();
1496                            break;
1497
1498                        case DUMP_V8COUNTERS:
1499                            nativeDumpV8Counters();
1500                            break;
1501
1502                        case SET_JS_FLAGS:
1503                            nativeSetJsFlags((String)msg.obj);
1504                            break;
1505
1506                        case SAVE_WEBARCHIVE:
1507                            WebView.SaveWebArchiveMessage saveMessage =
1508                                (WebView.SaveWebArchiveMessage)msg.obj;
1509                            saveMessage.mResultFile =
1510                                saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname);
1511                            mWebView.mPrivateHandler.obtainMessage(
1512                                WebView.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
1513                            break;
1514
1515                        case GEOLOCATION_PERMISSIONS_PROVIDE:
1516                            GeolocationPermissionsData data =
1517                                    (GeolocationPermissionsData) msg.obj;
1518                            nativeGeolocationPermissionsProvide(data.mOrigin,
1519                                    data.mAllow, data.mRemember);
1520                            break;
1521
1522                        case SPLIT_PICTURE_SET:
1523                            nativeSplitContent(msg.arg1);
1524                            mWebView.mPrivateHandler.obtainMessage(
1525                                    WebView.REPLACE_BASE_CONTENT, msg.arg1, 0);
1526                            mSplitPictureIsScheduled = false;
1527                            break;
1528
1529                        case CLEAR_CONTENT:
1530                            // Clear the view so that onDraw() will draw nothing
1531                            // but white background
1532                            // (See public method WebView.clearView)
1533                            nativeClearContent();
1534                            break;
1535
1536                        case MESSAGE_RELAY:
1537                            ((Message) msg.obj).sendToTarget();
1538                            break;
1539
1540                        case POPULATE_VISITED_LINKS:
1541                            nativeProvideVisitedHistory((String[])msg.obj);
1542                            break;
1543
1544                        case VALID_NODE_BOUNDS: {
1545                            MotionUpData motionUpData = (MotionUpData) msg.obj;
1546                            if (!nativeValidNodeAndBounds(
1547                                    motionUpData.mFrame, motionUpData.mNode,
1548                                    motionUpData.mBounds)) {
1549                                nativeUpdateFrameCache();
1550                            }
1551                            Message message = mWebView.mPrivateHandler
1552                                    .obtainMessage(WebView.DO_MOTION_UP,
1553                                    motionUpData.mX, motionUpData.mY);
1554                            mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(
1555                                    message);
1556                            break;
1557                        }
1558
1559                        case HIDE_FULLSCREEN:
1560                            nativeFullScreenPluginHidden(msg.arg1);
1561                            break;
1562
1563                        case ADD_PACKAGE_NAMES:
1564                            if (BrowserFrame.sJavaBridge == null) {
1565                                throw new IllegalStateException("No WebView " +
1566                                        "has been created in this process!");
1567                            }
1568                            BrowserFrame.sJavaBridge.addPackageNames(
1569                                    (Set<String>) msg.obj);
1570                            break;
1571
1572                        case GET_TOUCH_HIGHLIGHT_RECTS:
1573                            TouchHighlightData d = (TouchHighlightData) msg.obj;
1574                            ArrayList<Rect> rects = nativeGetTouchHighlightRects
1575                                    (d.mX, d.mY, d.mSlop);
1576                            mWebView.mPrivateHandler.obtainMessage(
1577                                    WebView.SET_TOUCH_HIGHLIGHT_RECTS, rects)
1578                                    .sendToTarget();
1579                            break;
1580
1581                        case REMOVE_TOUCH_HIGHLIGHT_RECTS:
1582                            mWebView.mPrivateHandler.obtainMessage(
1583                                    WebView.SET_TOUCH_HIGHLIGHT_RECTS, null)
1584                                    .sendToTarget();
1585                            break;
1586
1587                        case USE_MOCK_DEVICE_ORIENTATION:
1588                            useMockDeviceOrientation();
1589                            break;
1590
1591                        case AUTOFILL_FORM:
1592                            nativeAutoFillForm(msg.arg1);
1593                            mWebView.mPrivateHandler.obtainMessage(WebView.AUTOFILL_COMPLETE, null)
1594                                    .sendToTarget();
1595                            break;
1596
1597                        case EXECUTE_JS:
1598                            if (msg.obj instanceof String) {
1599                                if (DebugFlags.WEB_VIEW_CORE) {
1600                                    Log.d(LOGTAG, "Executing JS : " + msg.obj);
1601                                }
1602                                mBrowserFrame.stringByEvaluatingJavaScriptFromString((String) msg.obj);
1603                            }
1604                            break;
1605                    }
1606                }
1607            };
1608            // Take all queued messages and resend them to the new handler.
1609            synchronized (this) {
1610                int size = mMessages.size();
1611                for (int i = 0; i < size; i++) {
1612                    mHandler.sendMessage(mMessages.get(i));
1613                }
1614                mMessages = null;
1615            }
1616        }
1617
1618        /**
1619         * Send a message internally to the queue or to the handler
1620         */
1621        private synchronized void sendMessage(Message msg) {
1622            if (mBlockMessages) {
1623                return;
1624            }
1625            if (mMessages != null) {
1626                mMessages.add(msg);
1627            } else {
1628                mHandler.sendMessage(msg);
1629            }
1630        }
1631
1632        private synchronized void removeMessages(int what) {
1633            if (mBlockMessages) {
1634                return;
1635            }
1636            if (what == EventHub.WEBKIT_DRAW) {
1637                mDrawIsScheduled = false;
1638            }
1639            if (mMessages != null) {
1640                Log.w(LOGTAG, "Not supported in this case.");
1641            } else {
1642                mHandler.removeMessages(what);
1643            }
1644        }
1645
1646        private synchronized boolean hasMessages(int what) {
1647            if (mBlockMessages) {
1648                return false;
1649            }
1650            if (mMessages != null) {
1651                Log.w(LOGTAG, "hasMessages() is not supported in this case.");
1652                return false;
1653            } else {
1654                return mHandler.hasMessages(what);
1655            }
1656        }
1657
1658        private synchronized void sendMessageDelayed(Message msg, long delay) {
1659            if (mBlockMessages) {
1660                return;
1661            }
1662            mHandler.sendMessageDelayed(msg, delay);
1663        }
1664
1665        /**
1666         * Send a message internally to the front of the queue.
1667         */
1668        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1669            if (mBlockMessages) {
1670                return;
1671            }
1672            if (mMessages != null) {
1673                mMessages.add(0, msg);
1674            } else {
1675                mHandler.sendMessageAtFrontOfQueue(msg);
1676            }
1677        }
1678
1679        /**
1680         * Remove all the messages.
1681         */
1682        private synchronized void removeMessages() {
1683            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1684            mDrawIsScheduled = false;
1685            mSplitPictureIsScheduled = false;
1686            if (mMessages != null) {
1687                mMessages.clear();
1688            } else {
1689                mHandler.removeCallbacksAndMessages(null);
1690            }
1691        }
1692
1693        /**
1694         * Block sending messages to the EventHub.
1695         */
1696        private synchronized void blockMessages() {
1697            mBlockMessages = true;
1698        }
1699    }
1700
1701    //-------------------------------------------------------------------------
1702    // Methods called by host activity (in the same thread)
1703    //-------------------------------------------------------------------------
1704
1705    void stopLoading() {
1706        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1707        if (mBrowserFrame != null) {
1708            mBrowserFrame.stopLoading();
1709        }
1710    }
1711
1712    //-------------------------------------------------------------------------
1713    // Methods called by WebView
1714    // If it refers to local variable, it needs synchronized().
1715    // If it needs WebCore, it has to send message.
1716    //-------------------------------------------------------------------------
1717
1718    /**
1719     * @hide
1720     */
1721    public void sendMessage(Message msg) {
1722        mEventHub.sendMessage(msg);
1723    }
1724
1725    void sendMessage(int what) {
1726        mEventHub.sendMessage(Message.obtain(null, what));
1727    }
1728
1729    void sendMessage(int what, Object obj) {
1730        mEventHub.sendMessage(Message.obtain(null, what, obj));
1731    }
1732
1733    void sendMessage(int what, int arg1) {
1734        // just ignore the second argument (make it 0)
1735        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1736    }
1737
1738    void sendMessage(int what, int arg1, int arg2) {
1739        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1740    }
1741
1742    void sendMessage(int what, int arg1, Object obj) {
1743        // just ignore the second argument (make it 0)
1744        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1745    }
1746
1747    void sendMessage(int what, int arg1, int arg2, Object obj) {
1748        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1749    }
1750
1751    void sendMessageAtFrontOfQueue(int what, Object obj) {
1752        mEventHub.sendMessageAtFrontOfQueue(Message.obtain(
1753                null, what, obj));
1754    }
1755
1756    void sendMessageDelayed(int what, Object obj, long delay) {
1757        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1758    }
1759
1760    void removeMessages(int what) {
1761        mEventHub.removeMessages(what);
1762    }
1763
1764    void removeMessages() {
1765        mEventHub.removeMessages();
1766    }
1767
1768    /**
1769     * Sends a DESTROY message to WebCore.
1770     * Called from UI thread.
1771     */
1772    void destroy() {
1773        synchronized (mEventHub) {
1774            // Do not call removeMessages as then we risk removing PAUSE_TIMERS
1775            // or RESUME_TIMERS messages, which we must still handle as they
1776            // are per process. DESTROY will instead trigger a white list in
1777            // mEventHub, skipping any remaining messages in the queue
1778            mEventHub.sendMessageAtFrontOfQueue(
1779                    Message.obtain(null, EventHub.DESTROY));
1780            mEventHub.blockMessages();
1781        }
1782    }
1783
1784    //-------------------------------------------------------------------------
1785    // WebViewCore private methods
1786    //-------------------------------------------------------------------------
1787
1788    private void clearCache(boolean includeDiskFiles) {
1789        mBrowserFrame.clearCache();
1790        if (includeDiskFiles) {
1791            CacheManager.removeAllCacheFiles();
1792        }
1793    }
1794
1795    private void loadUrl(String url, Map<String, String> extraHeaders) {
1796        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1797        mBrowserFrame.loadUrl(url, extraHeaders);
1798    }
1799
1800    private String saveWebArchive(String filename, boolean autoname) {
1801        if (DebugFlags.WEB_VIEW_CORE) {
1802            Log.v(LOGTAG, " CORE saveWebArchive " + filename + " " + autoname);
1803        }
1804        return mBrowserFrame.saveWebArchive(filename, autoname);
1805    }
1806
1807    private void key(KeyEvent evt, boolean isDown) {
1808        if (DebugFlags.WEB_VIEW_CORE) {
1809            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1810                    + evt);
1811        }
1812        int keyCode = evt.getKeyCode();
1813        int unicodeChar = evt.getUnicodeChar();
1814
1815        if (keyCode == KeyEvent.KEYCODE_UNKNOWN && evt.getCharacters() != null
1816                && evt.getCharacters().length() > 0) {
1817            // we should only receive individual complex characters
1818            unicodeChar = evt.getCharacters().codePointAt(0);
1819        }
1820
1821        if (!nativeKey(keyCode, unicodeChar, evt.getRepeatCount(), evt.isShiftPressed(),
1822                evt.isAltPressed(), evt.isSymPressed(),
1823                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1824            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1825                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1826                if (DebugFlags.WEB_VIEW_CORE) {
1827                    Log.v(LOGTAG, "key: arrow unused by page: " + keyCode);
1828                }
1829                if (mWebView != null && evt.isDown()) {
1830                    Message.obtain(mWebView.mPrivateHandler,
1831                            WebView.UNHANDLED_NAV_KEY, keyCode,
1832                            0).sendToTarget();
1833                }
1834                return;
1835            }
1836            // bubble up the event handling
1837            // but do not bubble up the ENTER key, which would open the search
1838            // bar without any text.
1839            mCallbackProxy.onUnhandledKeyEvent(evt);
1840        }
1841    }
1842
1843    // These values are used to avoid requesting a layout based on old values
1844    private int mCurrentViewWidth = 0;
1845    private int mCurrentViewHeight = 0;
1846    private float mCurrentViewScale = 1.0f;
1847
1848    // notify webkit that our virtual view size changed size (after inv-zoom)
1849    private void viewSizeChanged(WebView.ViewSizeData data) {
1850        int w = data.mWidth;
1851        int h = data.mHeight;
1852        int textwrapWidth = data.mTextWrapWidth;
1853        float scale = data.mScale;
1854        if (DebugFlags.WEB_VIEW_CORE) {
1855            Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
1856                    + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
1857        }
1858        if (w == 0) {
1859            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1860            return;
1861        }
1862        int width = calculateWindowWidth(w);
1863        int height = h;
1864        if (width != w) {
1865            float heightWidthRatio = data.mHeightWidthRatio;
1866            float ratio = (heightWidthRatio > 0) ? heightWidthRatio : (float) h / w;
1867            height = Math.round(ratio * width);
1868        }
1869        nativeSetSize(width, height, textwrapWidth, scale, w,
1870                data.mActualViewHeight > 0 ? data.mActualViewHeight : h,
1871                data.mAnchorX, data.mAnchorY, data.mIgnoreHeight);
1872        // Remember the current width and height
1873        boolean needInvalidate = (mCurrentViewWidth == 0);
1874        mCurrentViewWidth = w;
1875        mCurrentViewHeight = h;
1876        mCurrentViewScale = scale;
1877        if (needInvalidate) {
1878            // ensure {@link #webkitDraw} is called as we were blocking in
1879            // {@link #contentDraw} when mCurrentViewWidth is 0
1880            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1881            contentDraw();
1882        }
1883        mEventHub.sendMessage(Message.obtain(null,
1884                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1885    }
1886
1887    // Calculate width to be used in webkit window.
1888    private int calculateWindowWidth(int viewWidth) {
1889        int width = viewWidth;
1890        if (mSettings.getUseWideViewPort()) {
1891            if (mViewportWidth == -1) {
1892                // Fixed viewport width.
1893                width = WebView.DEFAULT_VIEWPORT_WIDTH;
1894            } else if (mViewportWidth > 0) {
1895                // Use website specified or desired fixed viewport width.
1896                width = mViewportWidth;
1897            } else {
1898                // For mobile web site.
1899                width = Math.round(mWebView.getViewWidth() / mWebView.getDefaultZoomScale());
1900            }
1901        }
1902        return width;
1903    }
1904
1905    private void sendUpdateTextEntry() {
1906        if (mWebView != null) {
1907            Message.obtain(mWebView.mPrivateHandler,
1908                    WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1909        }
1910    }
1911
1912    // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1913    // callbacks. Computes the sum of database quota for all origins.
1914    private long getUsedQuota() {
1915        WebStorage webStorage = WebStorage.getInstance();
1916        Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
1917
1918        if (origins == null) {
1919            return 0;
1920        }
1921        long usedQuota = 0;
1922        for (WebStorage.Origin website : origins) {
1923            usedQuota += website.getQuota();
1924        }
1925        return usedQuota;
1926    }
1927
1928    // called from UI thread
1929    void splitContent(int content) {
1930        if (!mSplitPictureIsScheduled) {
1931            mSplitPictureIsScheduled = true;
1932            sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
1933        }
1934    }
1935
1936    // Used to avoid posting more than one draw message.
1937    private boolean mDrawIsScheduled;
1938    private boolean mDrawLayersIsScheduled;
1939
1940    // Used to avoid posting more than one split picture message.
1941    private boolean mSplitPictureIsScheduled;
1942
1943    // Used to suspend drawing.
1944    private boolean mDrawIsPaused;
1945
1946    // mInitialViewState is set by didFirstLayout() and then reset in the
1947    // next webkitDraw after passing the state to the UI thread.
1948    private ViewState mInitialViewState = null;
1949    private boolean mFirstLayoutForNonStandardLoad;
1950
1951    static class ViewState {
1952        float mMinScale;
1953        float mMaxScale;
1954        float mViewScale;
1955        float mTextWrapScale;
1956        float mDefaultScale;
1957        int mScrollX;
1958        int mScrollY;
1959        boolean mMobileSite;
1960        boolean mIsRestored;
1961    }
1962
1963    static class DrawData {
1964        DrawData() {
1965            mBaseLayer = 0;
1966            mInvalRegion = new Region();
1967            mContentSize = new Point();
1968        }
1969        int mBaseLayer;
1970        Region mInvalRegion;
1971        // view size that was used by webkit during the most recent layout
1972        Point mViewSize;
1973        Point mContentSize;
1974        int mMinPrefWidth;
1975        // only non-null if it is for the first picture set after the first layout
1976        ViewState mViewState;
1977        boolean mFirstLayoutForNonStandardLoad;
1978        boolean mFocusSizeChanged;
1979    }
1980
1981    // Only update the layers' content, not the base surface
1982    // PictureSet.
1983    private void webkitDrawLayers() {
1984        mDrawLayersIsScheduled = false;
1985        if (mDrawIsScheduled) {
1986            removeMessages(EventHub.WEBKIT_DRAW);
1987            webkitDraw();
1988            return;
1989        }
1990        DrawData draw = new DrawData();
1991        draw.mBaseLayer = nativeUpdateLayers(draw.mInvalRegion);
1992        webkitDraw(draw);
1993    }
1994
1995    private void webkitDraw() {
1996        mDrawIsScheduled = false;
1997        DrawData draw = new DrawData();
1998        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
1999        draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mContentSize);
2000        if (draw.mBaseLayer == 0) {
2001            if (mWebView != null && !mWebView.isPaused()) {
2002                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message");
2003                mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
2004            } else {
2005                if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, webview paused");
2006            }
2007            return;
2008        }
2009        webkitDraw(draw);
2010    }
2011
2012    private void webkitDraw(DrawData draw) {
2013        if (mWebView != null) {
2014            draw.mFocusSizeChanged = nativeFocusBoundsChanged();
2015            draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight);
2016            if (mSettings.getUseWideViewPort()) {
2017                draw.mMinPrefWidth = Math.max(
2018                        mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH
2019                                : (mViewportWidth == 0 ? mCurrentViewWidth
2020                                        : mViewportWidth),
2021                        nativeGetContentMinPrefWidth());
2022            }
2023            if (mInitialViewState != null) {
2024                draw.mViewState = mInitialViewState;
2025                mInitialViewState = null;
2026            }
2027            if (mFirstLayoutForNonStandardLoad) {
2028                draw.mFirstLayoutForNonStandardLoad = true;
2029                mFirstLayoutForNonStandardLoad = false;
2030            }
2031            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
2032            Message.obtain(mWebView.mPrivateHandler,
2033                    WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
2034        }
2035    }
2036
2037    static void reducePriority() {
2038        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
2039        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
2040        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
2041        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
2042                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
2043    }
2044
2045    static void resumePriority() {
2046        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
2047        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
2048        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
2049        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
2050                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
2051    }
2052
2053    static void sendStaticMessage(int messageType, Object argument) {
2054        if (sWebCoreHandler == null)
2055            return;
2056
2057        sWebCoreHandler.sendMessage(sWebCoreHandler.obtainMessage(messageType, argument));
2058    }
2059
2060    static void pauseUpdatePicture(WebViewCore core) {
2061        // Note: there is one possible failure mode. If pauseUpdatePicture() is
2062        // called from UI thread while WEBKIT_DRAW is just pulled out of the
2063        // queue in WebCore thread to be executed. Then update won't be blocked.
2064        if (core != null) {
2065            if (!core.getSettings().enableSmoothTransition()) return;
2066
2067            synchronized (core) {
2068                core.mDrawIsPaused = true;
2069                if (core.mDrawIsScheduled) {
2070                    core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
2071                }
2072            }
2073        }
2074
2075    }
2076
2077    static void resumeUpdatePicture(WebViewCore core) {
2078        if (core != null) {
2079            // if mDrawIsPaused is true, ignore the setting, continue to resume
2080            if (!core.mDrawIsPaused
2081                    && !core.getSettings().enableSmoothTransition()) return;
2082
2083            synchronized (core) {
2084                core.mDrawIsPaused = false;
2085                // always redraw on resume to reenable gif animations
2086                core.mDrawIsScheduled = false;
2087                core.nativeContentInvalidateAll();
2088                core.contentDraw();
2089            }
2090        }
2091    }
2092
2093    //////////////////////////////////////////////////////////////////////////
2094
2095    private void restoreState(int index) {
2096        WebBackForwardList list = mCallbackProxy.getBackForwardList();
2097        int size = list.getSize();
2098        for (int i = 0; i < size; i++) {
2099            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
2100        }
2101        mBrowserFrame.mLoadInitFromJava = true;
2102        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
2103        mBrowserFrame.mLoadInitFromJava = false;
2104    }
2105
2106    //-------------------------------------------------------------------------
2107    // Implement abstract methods in WebViewCore, native WebKit callback part
2108    //-------------------------------------------------------------------------
2109
2110    // called from JNI or WebView thread
2111    /* package */ void contentDraw() {
2112        synchronized (this) {
2113            if (mWebView == null || mBrowserFrame == null) {
2114                // We were destroyed
2115                return;
2116            }
2117            // don't update the Picture until we have an initial width and finish
2118            // the first layout
2119            if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
2120                return;
2121            }
2122            // only fire an event if this is our first request
2123            if (mDrawIsScheduled) return;
2124            mDrawIsScheduled = true;
2125            if (mDrawIsPaused) return;
2126            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
2127        }
2128    }
2129
2130    // called from JNI
2131    void layersDraw() {
2132        synchronized (this) {
2133            if (mDrawLayersIsScheduled) return;
2134            mDrawLayersIsScheduled = true;
2135            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW_LAYERS));
2136        }
2137    }
2138
2139    // called by JNI
2140    private void contentScrollTo(int x, int y, boolean animate,
2141            boolean onlyIfImeIsShowing) {
2142        if (!mBrowserFrame.firstLayoutDone()) {
2143            /*
2144             * WebKit restore state will be called before didFirstLayout(),
2145             * remember the position as it has to be applied after restoring
2146             * zoom factor which is controlled by screenWidth.
2147             */
2148            mRestoredX = x;
2149            mRestoredY = y;
2150            return;
2151        }
2152        if (mWebView != null) {
2153            Message msg = Message.obtain(mWebView.mPrivateHandler,
2154                    WebView.SCROLL_TO_MSG_ID, animate ? 1 : 0,
2155                    onlyIfImeIsShowing ? 1 : 0, new Point(x, y));
2156            if (mDrawIsScheduled) {
2157                mEventHub.sendMessage(Message.obtain(null,
2158                        EventHub.MESSAGE_RELAY, msg));
2159            } else {
2160                msg.sendToTarget();
2161            }
2162        }
2163    }
2164
2165    // called by JNI
2166    private void sendNotifyProgressFinished() {
2167        sendUpdateTextEntry();
2168        if (!JniUtil.useChromiumHttpStack()) {
2169            // as CacheManager can behave based on database transaction, we need to
2170            // call tick() to trigger endTransaction
2171            WebViewWorker.getHandler().removeMessages(
2172                    WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
2173            WebViewWorker.getHandler().sendEmptyMessage(
2174                    WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
2175        }
2176        contentDraw();
2177    }
2178
2179    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
2180        be scaled before they can be used by the view system, which happens
2181        in WebView since it (and its thread) know the current scale factor.
2182     */
2183    private void sendViewInvalidate(int left, int top, int right, int bottom) {
2184        if (mWebView != null) {
2185            Message.obtain(mWebView.mPrivateHandler,
2186                           WebView.INVAL_RECT_MSG_ID,
2187                           new Rect(left, top, right, bottom)).sendToTarget();
2188        }
2189    }
2190
2191    private static boolean mRepaintScheduled = false;
2192
2193    /*
2194     * Called by the WebView thread
2195     */
2196    /* package */ void signalRepaintDone() {
2197        mRepaintScheduled = false;
2198    }
2199
2200    /* package */ WebView getWebView() {
2201        return mWebView;
2202    }
2203
2204    private native void setViewportSettingsFromNative();
2205
2206    // called by JNI
2207    private void didFirstLayout(boolean standardLoad) {
2208        if (DebugFlags.WEB_VIEW_CORE) {
2209            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
2210        }
2211
2212        mBrowserFrame.didFirstLayout();
2213
2214        if (mWebView == null) return;
2215
2216        boolean updateViewState = standardLoad || mRestoredScale > 0;
2217        setupViewport(updateViewState);
2218        // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will
2219        // be called after the WebView updates its state. If updateRestoreState
2220        // is false, start to draw now as it is ready.
2221        if (!updateViewState) {
2222            mWebView.mViewManager.postReadyToDrawAll();
2223        }
2224
2225        // remove the touch highlight when moving to a new page
2226        if (getSettings().supportTouchOnly()) {
2227            mEventHub.sendMessage(Message.obtain(null,
2228                    EventHub.REMOVE_TOUCH_HIGHLIGHT_RECTS));
2229        }
2230
2231        // reset the scroll position, the restored offset and scales
2232        mRestoredX = mRestoredY = 0;
2233        mRestoredScale = mRestoredTextWrapScale = 0;
2234    }
2235
2236    // called by JNI
2237    private void updateViewport() {
2238        // Update viewport asap to make sure we get correct one.
2239        setupViewport(true);
2240    }
2241
2242    private void setupViewport(boolean updateViewState) {
2243        if (mWebView == null || mSettings == null) {
2244            // We've been destroyed or are being destroyed, return early
2245            return;
2246        }
2247        // set the viewport settings from WebKit
2248        setViewportSettingsFromNative();
2249
2250        if (mSettings.forceUserScalable()) {
2251            mViewportUserScalable = true;
2252            if (mViewportInitialScale > 0) {
2253                if (mViewportMinimumScale > 0) {
2254                    mViewportMinimumScale = Math.min(mViewportMinimumScale,
2255                            mViewportInitialScale / 2);
2256                }
2257                if (mViewportMaximumScale > 0) {
2258                    mViewportMaximumScale = Math.max(mViewportMaximumScale,
2259                            mViewportInitialScale * 2);
2260                }
2261            } else {
2262                if (mViewportMinimumScale > 0) {
2263                    mViewportMinimumScale = Math.min(mViewportMinimumScale, 50);
2264                }
2265                if (mViewportMaximumScale > 0) {
2266                    mViewportMaximumScale = Math.max(mViewportMaximumScale, 200);
2267                }
2268            }
2269        }
2270
2271        // adjust the default scale to match the densityDpi
2272        float adjust = 1.0f;
2273        if (mViewportDensityDpi == -1) {
2274            // convert default zoom scale to a integer (percentage) to avoid any
2275            // issues with floating point comparisons
2276            if (mWebView != null && (int)(mWebView.getDefaultZoomScale() * 100) != 100) {
2277                adjust = mWebView.getDefaultZoomScale();
2278            }
2279        } else if (mViewportDensityDpi > 0) {
2280            adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
2281                    / mViewportDensityDpi;
2282        }
2283        int defaultScale = (int) (adjust * 100);
2284
2285        if (mViewportInitialScale > 0) {
2286            mViewportInitialScale *= adjust;
2287        }
2288        if (mViewportMinimumScale > 0) {
2289            mViewportMinimumScale *= adjust;
2290        }
2291        if (mViewportMaximumScale > 0) {
2292            mViewportMaximumScale *= adjust;
2293        }
2294
2295        // infer the values if they are not defined.
2296        if (mViewportWidth == 0) {
2297            if (mViewportInitialScale == 0) {
2298                mViewportInitialScale = defaultScale;
2299            }
2300        }
2301        if (mViewportUserScalable == false) {
2302            mViewportInitialScale = defaultScale;
2303            mViewportMinimumScale = defaultScale;
2304            mViewportMaximumScale = defaultScale;
2305        }
2306        if (mViewportMinimumScale > mViewportInitialScale
2307                && mViewportInitialScale != 0) {
2308            mViewportMinimumScale = mViewportInitialScale;
2309        }
2310        if (mViewportMaximumScale > 0
2311                && mViewportMaximumScale < mViewportInitialScale) {
2312            mViewportMaximumScale = mViewportInitialScale;
2313        }
2314        if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
2315            mViewportWidth = 0;
2316        }
2317
2318        // if mViewportWidth is 0, it means device-width, always update.
2319        if (mViewportWidth != 0 && !updateViewState) {
2320            // For non standard load, since updateViewState will be false.
2321            mFirstLayoutForNonStandardLoad = true;
2322            ViewState viewState = new ViewState();
2323            viewState.mMinScale = mViewportMinimumScale / 100.0f;
2324            viewState.mMaxScale = mViewportMaximumScale / 100.0f;
2325            viewState.mDefaultScale = adjust;
2326            // as mViewportWidth is not 0, it is not mobile site.
2327            viewState.mMobileSite = false;
2328            // for non-mobile site, we don't need minPrefWidth, set it as 0
2329            viewState.mScrollX = 0;
2330            Message.obtain(mWebView.mPrivateHandler,
2331                    WebView.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
2332            return;
2333        }
2334
2335        // now notify webview
2336        // webViewWidth refers to the width in the view system
2337        int webViewWidth;
2338        // viewportWidth refers to the width in the document system
2339        int viewportWidth = mCurrentViewWidth;
2340        if (viewportWidth == 0) {
2341            // this may happen when WebView just starts. This is not perfect as
2342            // we call WebView method from WebCore thread. But not perfect
2343            // reference is better than no reference.
2344            webViewWidth = mWebView.getViewWidth();
2345            viewportWidth = (int) (webViewWidth / adjust);
2346            if (viewportWidth == 0) {
2347                if (DebugFlags.WEB_VIEW_CORE) {
2348                    Log.v(LOGTAG, "Can't get the viewWidth yet");
2349                }
2350            }
2351        } else {
2352            webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
2353        }
2354        mInitialViewState = new ViewState();
2355        mInitialViewState.mMinScale = mViewportMinimumScale / 100.0f;
2356        mInitialViewState.mMaxScale = mViewportMaximumScale / 100.0f;
2357        mInitialViewState.mDefaultScale = adjust;
2358        mInitialViewState.mScrollX = mRestoredX;
2359        mInitialViewState.mScrollY = mRestoredY;
2360        mInitialViewState.mMobileSite = (0 == mViewportWidth);
2361        if (mRestoredScale > 0) {
2362            mInitialViewState.mIsRestored = true;
2363            mInitialViewState.mViewScale = mRestoredScale;
2364            if (mRestoredTextWrapScale > 0) {
2365                mInitialViewState.mTextWrapScale = mRestoredTextWrapScale;
2366            } else {
2367                mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale;
2368            }
2369        } else {
2370            if (mViewportInitialScale > 0) {
2371                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
2372                        mViewportInitialScale / 100.0f;
2373            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth &&
2374                !getSettings().getUseFixedViewport()) {
2375                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
2376                        (float) webViewWidth / mViewportWidth;
2377            } else {
2378                mInitialViewState.mTextWrapScale = adjust;
2379                if (mSettings.getUseWideViewPort()) {
2380                    // 0 will trigger WebView to turn on zoom overview mode
2381                    mInitialViewState.mViewScale = 0;
2382                } else {
2383                    mInitialViewState.mViewScale = adjust;
2384                }
2385            }
2386        }
2387
2388        if (mWebView.mHeightCanMeasure) {
2389            // Trick to ensure that the Picture has the exact height for the
2390            // content by forcing to layout with 0 height after the page is
2391            // ready, which is indicated by didFirstLayout. This is essential to
2392            // get rid of the white space in the GMail which uses WebView for
2393            // message view.
2394            mWebView.mLastHeightSent = 0;
2395            // Send a negative scale to indicate that WebCore should reuse
2396            // the current scale
2397            WebView.ViewSizeData data = new WebView.ViewSizeData();
2398            data.mWidth = mWebView.mLastWidthSent;
2399            data.mHeight = 0;
2400            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
2401            // true. It is safe to use mWidth for mTextWrapWidth.
2402            data.mTextWrapWidth = data.mWidth;
2403            data.mScale = -1.0f;
2404            data.mIgnoreHeight = false;
2405            data.mAnchorX = data.mAnchorY = 0;
2406            // send VIEW_SIZE_CHANGED to the front of the queue so that we can
2407            // avoid pushing the wrong picture to the WebView side. If there is
2408            // a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
2409            // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED
2410            // in the queue, as mLastHeightSent has been updated here, we may
2411            // miss the requestLayout in WebView side after the new picture.
2412            mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2413            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2414                    EventHub.VIEW_SIZE_CHANGED, data));
2415        } else {
2416            if (viewportWidth == 0) {
2417                // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
2418                // to WebViewCore
2419                mWebView.mLastWidthSent = 0;
2420            } else {
2421                WebView.ViewSizeData data = new WebView.ViewSizeData();
2422                // mViewScale as 0 means it is in zoom overview mode. So we don't
2423                // know the exact scale. If mRestoredScale is non-zero, use it;
2424                // otherwise just use mTextWrapScale as the initial scale.
2425                float tentativeScale = mInitialViewState.mViewScale;
2426                if (tentativeScale == 0) {
2427                    // The following tries to figure out more correct view scale
2428                    // and text wrap scale to be sent to webkit, by using some
2429                    // knowledge from web settings and zoom manager.
2430
2431                    // Calculated window width will be used to guess the scale
2432                    // in zoom overview mode.
2433                    tentativeScale = mInitialViewState.mTextWrapScale;
2434                    int tentativeViewWidth = Math.round(webViewWidth / tentativeScale);
2435                    int windowWidth = calculateWindowWidth(tentativeViewWidth);
2436                    // In viewport setup time, since no content width is known, we assume
2437                    // the windowWidth will be the content width, to get a more likely
2438                    // zoom overview scale.
2439                    data.mScale = (float) webViewWidth / windowWidth;
2440                    if (!mSettings.getLoadWithOverviewMode()) {
2441                        // If user choose non-overview mode.
2442                        data.mScale = Math.max(data.mScale, tentativeScale);
2443                    }
2444                    if (mSettings.isNarrowColumnLayout()) {
2445                        // In case of automatic text reflow in fixed view port mode.
2446                        mInitialViewState.mTextWrapScale =
2447                                ZoomManager.computeReadingLevelScale(data.mScale);
2448                    }
2449                } else {
2450                    // Scale is given such as when page is restored, use it.
2451                    data.mScale = tentativeScale;
2452                }
2453                if (DebugFlags.WEB_VIEW_CORE) {
2454                    Log.v(LOGTAG, "setupViewport"
2455                             + " mRestoredScale=" + mRestoredScale
2456                             + " mViewScale=" + mInitialViewState.mViewScale
2457                             + " mTextWrapScale=" + mInitialViewState.mTextWrapScale
2458                             + " data.mScale= " + data.mScale
2459                             );
2460                }
2461                data.mWidth = Math.round(webViewWidth / data.mScale);
2462                // We may get a call here when mCurrentViewHeight == 0 if webcore completes the
2463                // first layout before we sync our webview dimensions to it. In that case, we
2464                // request the real height of the webview. This is not a perfect solution as we
2465                // are calling a WebView method from the WebCore thread. But this is preferable
2466                // to syncing an incorrect height.
2467                data.mHeight = mCurrentViewHeight == 0 ?
2468                        Math.round(mWebView.getViewHeight() / data.mScale)
2469                        : Math.round((float) mCurrentViewHeight * data.mWidth / viewportWidth);
2470                data.mTextWrapWidth = Math.round(webViewWidth
2471                        / mInitialViewState.mTextWrapScale);
2472                data.mIgnoreHeight = false;
2473                data.mAnchorX = data.mAnchorY = 0;
2474                // send VIEW_SIZE_CHANGED to the front of the queue so that we
2475                // can avoid pushing the wrong picture to the WebView side.
2476                mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2477                // Let webkit know the scale and inner width/height immediately
2478                // in viewport setup time to avoid wrong information.
2479                viewSizeChanged(data);
2480            }
2481        }
2482    }
2483
2484    // called by JNI
2485    private void restoreScale(float scale, float textWrapScale) {
2486        if (mBrowserFrame.firstLayoutDone() == false) {
2487            final float defaultScale = mWebView.getDefaultZoomScale();
2488            mRestoredScale = (scale <= 0.0) ? defaultScale : scale;
2489            if (mSettings.getUseWideViewPort()) {
2490                mRestoredTextWrapScale = (textWrapScale <= 0.0) ? defaultScale : textWrapScale;
2491            }
2492        }
2493    }
2494
2495    // called by JNI
2496    private void needTouchEvents(boolean need) {
2497        if (mWebView != null) {
2498            Message.obtain(mWebView.mPrivateHandler,
2499                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
2500                    .sendToTarget();
2501        }
2502    }
2503
2504    // called by JNI
2505    private void updateTextfield(int ptr, boolean changeToPassword,
2506            String text, int textGeneration) {
2507        if (mWebView != null) {
2508            Message msg = Message.obtain(mWebView.mPrivateHandler,
2509                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
2510                    textGeneration, text);
2511            msg.getData().putBoolean("password", changeToPassword);
2512            msg.sendToTarget();
2513        }
2514    }
2515
2516    // called by JNI
2517    private void updateTextSelection(int pointer, int start, int end,
2518            int textGeneration) {
2519        if (mWebView != null) {
2520            Message.obtain(mWebView.mPrivateHandler,
2521                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
2522                new TextSelectionData(start, end)).sendToTarget();
2523        }
2524    }
2525
2526    // called by JNI
2527    private void clearTextEntry() {
2528        if (mWebView == null) return;
2529        Message.obtain(mWebView.mPrivateHandler,
2530                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
2531    }
2532
2533    // called by JNI
2534    private void sendFindAgain() {
2535        if (mWebView == null) return;
2536        Message.obtain(mWebView.mPrivateHandler,
2537                WebView.FIND_AGAIN).sendToTarget();
2538    }
2539
2540    private native void nativeUpdateFrameCacheIfLoading();
2541    private native void nativeRevealSelection();
2542    private native String nativeRequestLabel(int framePtr, int nodePtr);
2543    /**
2544     * Scroll the focused textfield to (xPercent, y) in document space
2545     */
2546    private native void nativeScrollFocusedTextInput(float xPercent, int y);
2547
2548    // these must be in document space (i.e. not scaled/zoomed).
2549    private native void nativeSetScrollOffset(int gen, boolean sendScrollEvent, int dx, int dy);
2550
2551    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
2552
2553    // called by JNI
2554    private void requestListBox(String[] array, int[] enabledArray,
2555            int[] selectedArray) {
2556        if (mWebView != null) {
2557            mWebView.requestListBox(array, enabledArray, selectedArray);
2558        }
2559    }
2560
2561    // called by JNI
2562    private void requestListBox(String[] array, int[] enabledArray,
2563            int selection) {
2564        if (mWebView != null) {
2565            mWebView.requestListBox(array, enabledArray, selection);
2566        }
2567
2568    }
2569
2570    // called by JNI
2571    private void requestKeyboardWithSelection(int pointer, int selStart,
2572            int selEnd, int textGeneration) {
2573        if (mWebView != null) {
2574            Message.obtain(mWebView.mPrivateHandler,
2575                    WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
2576                    textGeneration, new TextSelectionData(selStart, selEnd))
2577                    .sendToTarget();
2578        }
2579    }
2580
2581    // called by JNI
2582    private void requestKeyboard(boolean showKeyboard) {
2583        if (mWebView != null) {
2584            Message.obtain(mWebView.mPrivateHandler,
2585                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
2586                    .sendToTarget();
2587        }
2588    }
2589
2590    private void setWebTextViewAutoFillable(int queryId, String preview) {
2591        if (mWebView != null) {
2592            Message.obtain(mWebView.mPrivateHandler, WebView.SET_AUTOFILLABLE,
2593                    new AutoFillData(queryId, preview))
2594                    .sendToTarget();
2595        }
2596    }
2597
2598    // called by JNI
2599    private Context getContext() {
2600        return mContext;
2601    }
2602
2603    // called by JNI
2604    private void keepScreenOn(boolean screenOn) {
2605        if (mWebView != null) {
2606            Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SCREEN_ON);
2607            message.arg1 = screenOn ? 1 : 0;
2608            message.sendToTarget();
2609        }
2610    }
2611
2612    // called by JNI
2613    private Class<?> getPluginClass(String libName, String clsName) {
2614
2615        if (mWebView == null) {
2616            return null;
2617        }
2618
2619        PluginManager pluginManager = PluginManager.getInstance(null);
2620
2621        String pkgName = pluginManager.getPluginsAPKName(libName);
2622        if (pkgName == null) {
2623            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2624            return null;
2625        }
2626
2627        try {
2628            return pluginManager.getPluginClass(pkgName, clsName);
2629        } catch (NameNotFoundException e) {
2630            Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")");
2631        } catch (ClassNotFoundException e) {
2632            Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
2633                    ") in the apk (" + pkgName + ")");
2634        }
2635
2636        return null;
2637    }
2638
2639    // called by JNI. PluginWidget function to launch a full-screen view using a
2640    // View object provided by the plugin class.
2641    private void showFullScreenPlugin(ViewManager.ChildView childView, int orientation, int npp) {
2642        if (mWebView == null) {
2643            return;
2644        }
2645
2646        Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN);
2647        message.obj = childView.mView;
2648        message.arg1 = orientation;
2649        message.arg2 = npp;
2650        message.sendToTarget();
2651    }
2652
2653    // called by JNI
2654    private void hideFullScreenPlugin() {
2655        if (mWebView == null) {
2656            return;
2657        }
2658        mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
2659                .sendToTarget();
2660    }
2661
2662    private ViewManager.ChildView createSurface(View pluginView) {
2663        if (mWebView == null) {
2664            return null;
2665        }
2666
2667        if (pluginView == null) {
2668            Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy");
2669            return null;
2670        }
2671
2672        // ensures the view system knows the view can redraw itself
2673        pluginView.setWillNotDraw(false);
2674
2675        if(pluginView instanceof SurfaceView)
2676            ((SurfaceView)pluginView).setZOrderOnTop(true);
2677
2678        ViewManager.ChildView view = mWebView.mViewManager.createView();
2679        view.mView = pluginView;
2680        return view;
2681    }
2682
2683    // called by JNI.  PluginWidget functions for creating an embedded View for
2684    // the surface drawing model.
2685    private ViewManager.ChildView addSurface(View pluginView, int x, int y,
2686                                             int width, int height) {
2687        ViewManager.ChildView view = createSurface(pluginView);
2688        view.attachView(x, y, width, height);
2689        return view;
2690    }
2691
2692    private void updateSurface(ViewManager.ChildView childView, int x, int y,
2693            int width, int height) {
2694        childView.attachView(x, y, width, height);
2695    }
2696
2697    private void destroySurface(ViewManager.ChildView childView) {
2698        childView.removeView();
2699    }
2700
2701    // called by JNI
2702    static class ShowRectData {
2703        int mLeft;
2704        int mTop;
2705        int mWidth;
2706        int mHeight;
2707        int mContentWidth;
2708        int mContentHeight;
2709        float mXPercentInDoc;
2710        float mXPercentInView;
2711        float mYPercentInDoc;
2712        float mYPercentInView;
2713    }
2714
2715    private void showRect(int left, int top, int width, int height,
2716            int contentWidth, int contentHeight, float xPercentInDoc,
2717            float xPercentInView, float yPercentInDoc, float yPercentInView) {
2718        if (mWebView != null) {
2719            ShowRectData data = new ShowRectData();
2720            data.mLeft = left;
2721            data.mTop = top;
2722            data.mWidth = width;
2723            data.mHeight = height;
2724            data.mContentWidth = contentWidth;
2725            data.mContentHeight = contentHeight;
2726            data.mXPercentInDoc = xPercentInDoc;
2727            data.mXPercentInView = xPercentInView;
2728            data.mYPercentInDoc = yPercentInDoc;
2729            data.mYPercentInView = yPercentInView;
2730            Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID,
2731                    data).sendToTarget();
2732        }
2733    }
2734
2735    // called by JNI
2736    private void centerFitRect(int x, int y, int width, int height) {
2737        if (mWebView == null) {
2738            return;
2739        }
2740        mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT,
2741                new Rect(x, y, x + width, y + height)).sendToTarget();
2742    }
2743
2744    // called by JNI
2745    private void setScrollbarModes(int hMode, int vMode) {
2746        if (mWebView == null) {
2747            return;
2748        }
2749        mWebView.mPrivateHandler.obtainMessage(WebView.SET_SCROLLBAR_MODES,
2750                hMode, vMode).sendToTarget();
2751    }
2752
2753    // called by JNI
2754    @SuppressWarnings("unused")
2755    private void selectAt(int x, int y) {
2756        if (mWebView != null) {
2757            mWebView.mPrivateHandler.obtainMessage(WebView.SELECT_AT, x, y).sendToTarget();
2758        }
2759    }
2760
2761    private void useMockDeviceOrientation() {
2762        mDeviceMotionAndOrientationManager.useMock();
2763    }
2764
2765    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
2766            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
2767        mDeviceMotionAndOrientationManager.setMockOrientation(canProvideAlpha, alpha,
2768                canProvideBeta, beta, canProvideGamma, gamma);
2769    }
2770
2771    protected DeviceMotionService getDeviceMotionService() {
2772        if (mDeviceMotionService == null) {
2773            mDeviceMotionService =
2774                    new DeviceMotionService(mDeviceMotionAndOrientationManager, mContext);
2775        }
2776        return mDeviceMotionService;
2777    }
2778
2779    protected DeviceOrientationService getDeviceOrientationService() {
2780        if (mDeviceOrientationService == null) {
2781            mDeviceOrientationService =
2782                    new DeviceOrientationService(mDeviceMotionAndOrientationManager, mContext);
2783        }
2784        return mDeviceOrientationService;
2785    }
2786
2787    private native void nativePause();
2788    private native void nativeResume();
2789    private native void nativeFreeMemory();
2790    private native void nativeFullScreenPluginHidden(int npp);
2791    private native boolean nativeValidNodeAndBounds(int frame, int node,
2792            Rect bounds);
2793
2794    private native ArrayList<Rect> nativeGetTouchHighlightRects(int x, int y,
2795            int slop);
2796
2797   private native void nativeAutoFillForm(int queryId);
2798   private native void nativeScrollLayer(int layer, Rect rect);
2799}
2800