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