WebViewCore.java revision cedb3a7e5849fd16e939add1ac6f5586467b8c68
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        System.loadLibrary("webcore");
60    }
61
62    /*
63     * WebViewCore always executes in the same thread as the native webkit.
64     */
65
66    // The WebView that corresponds to this WebViewCore.
67    private WebView mWebView;
68    // Proxy for handling callbacks from native code
69    private final CallbackProxy mCallbackProxy;
70    // Settings object for maintaining all settings
71    private final WebSettings mSettings;
72    // Context for initializing the BrowserFrame with the proper assets.
73    private final Context mContext;
74    // The pointer to a native view object.
75    private int mNativeClass;
76    // The BrowserFrame is an interface to the native Frame component.
77    private BrowserFrame mBrowserFrame;
78    // Custom JS interfaces to add during the initialization.
79    private Map<String, Object> mJavascriptInterfaces;
80    /*
81     * range is from 200 to 10,000. 0 is a special value means device-width. -1
82     * means undefined.
83     */
84    private int mViewportWidth = -1;
85
86    /*
87     * range is from 200 to 10,000. 0 is a special value means device-height. -1
88     * means undefined.
89     */
90    private int mViewportHeight = -1;
91
92    /*
93     * scale in percent, range is from 1 to 1000. 0 means undefined.
94     */
95    private int mViewportInitialScale = 0;
96
97    /*
98     * scale in percent, range is from 1 to 1000. 0 means undefined.
99     */
100    private int mViewportMinimumScale = 0;
101
102    /*
103     * scale in percent, range is from 1 to 1000. 0 means undefined.
104     */
105    private int mViewportMaximumScale = 0;
106
107    private boolean mViewportUserScalable = true;
108
109    /*
110     * range is from 70 to 400.
111     * 0 is a special value means device-dpi. The default scale factor will be
112     * always 100.
113     * -1 means undefined. The default scale factor will be
114     * WebView.DEFAULT_SCALE_PERCENT.
115     */
116    private int mViewportDensityDpi = -1;
117
118    private float mRestoredScale = 0;
119    private float mRestoredTextWrapScale = 0;
120    private int mRestoredX = 0;
121    private int mRestoredY = 0;
122
123    private int mWebkitScrollX = 0;
124    private int mWebkitScrollY = 0;
125
126    private DeviceMotionAndOrientationManager mDeviceMotionAndOrientationManager =
127            new DeviceMotionAndOrientationManager(this);
128    private DeviceMotionService mDeviceMotionService;
129    private DeviceOrientationService mDeviceOrientationService;
130
131    private int mLowMemoryUsageThresholdMb;
132    private int mHighMemoryUsageThresholdMb;
133
134    // The thread name used to identify the WebCore thread and for use in
135    // debugging other classes that require operation within the WebCore thread.
136    /* package */ static final String THREAD_NAME = "WebViewCoreThread";
137
138    public WebViewCore(Context context, WebView w, CallbackProxy proxy,
139            Map<String, Object> javascriptInterfaces) {
140        // No need to assign this in the WebCore thread.
141        mCallbackProxy = proxy;
142        mWebView = w;
143        mJavascriptInterfaces = javascriptInterfaces;
144        // This context object is used to initialize the WebViewCore during
145        // subwindow creation.
146        mContext = context;
147
148        // We need to wait for the initial thread creation before sending
149        // a message to the WebCore thread.
150        // XXX: This is the only time the UI thread will wait for the WebCore
151        // thread!
152        synchronized (WebViewCore.class) {
153            if (sWebCoreHandler == null) {
154                // Create a global thread and start it.
155                Thread t = new Thread(new WebCoreThread());
156                t.setName(THREAD_NAME);
157                t.start();
158                try {
159                    WebViewCore.class.wait();
160                } catch (InterruptedException e) {
161                    Log.e(LOGTAG, "Caught exception while waiting for thread " +
162                           "creation.");
163                    Log.e(LOGTAG, Log.getStackTraceString(e));
164                }
165            }
166        }
167        // Create an EventHub to handle messages before and after the thread is
168        // ready.
169        mEventHub = new EventHub();
170        // Create a WebSettings object for maintaining all settings
171        mSettings = new WebSettings(mContext, mWebView);
172        // The WebIconDatabase needs to be initialized within the UI thread so
173        // just request the instance here.
174        WebIconDatabase.getInstance();
175        // Create the WebStorage singleton and the UI handler
176        WebStorage.getInstance().createUIHandler();
177        // Create the UI handler for GeolocationPermissions
178        GeolocationPermissions.getInstance().createUIHandler();
179
180        // Get the memory class of the current device. V8 will use these values
181        // to GC more effectively.
182        ActivityManager manager = (ActivityManager) mContext.getSystemService(
183                Context.ACTIVITY_SERVICE);
184        ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
185        manager.getMemoryInfo(memInfo);
186
187        // Allow us to use up to our memory class value before V8's GC kicks in.
188        // These values have been determined by experimentation.
189        mLowMemoryUsageThresholdMb = manager.getMemoryClass();
190        // If things get crazy, allow V8 to use up to 3 times our memory class, or a third of the
191        // device's total available memory, whichever is smaller. At that point V8 will start
192        // attempting more aggressive garbage collection.
193        mHighMemoryUsageThresholdMb = Math.min(mLowMemoryUsageThresholdMb * 3,
194                (int) (memInfo.availMem / 3) >> 20);
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    /**
641     * Moves the selection to given node i.e. selects that node.
642     *
643     * Note: Accessibility support.
644     *
645     * @param framePtr Pointer to the frame containing the node to be selected.
646     * @param nodePtr Pointer to the node to be selected.
647     *
648     * @return The selection string.
649     */
650    private native String nativeMoveSelection(int framePtr, int nodePtr);
651
652    // EventHub for processing messages
653    private final EventHub mEventHub;
654    // WebCore thread handler
655    private static Handler sWebCoreHandler;
656    // Class for providing Handler creation inside the WebCore thread.
657    private static class WebCoreThread implements Runnable {
658        // Message id for initializing a new WebViewCore.
659        private static final int INITIALIZE = 0;
660        private static final int REDUCE_PRIORITY = 1;
661        private static final int RESUME_PRIORITY = 2;
662
663        public void run() {
664            Looper.prepare();
665            Assert.assertNull(sWebCoreHandler);
666            synchronized (WebViewCore.class) {
667                sWebCoreHandler = new Handler() {
668                    @Override
669                    public void handleMessage(Message msg) {
670                        switch (msg.what) {
671                            case INITIALIZE:
672                                WebViewCore core = (WebViewCore) msg.obj;
673                                core.initialize();
674                                break;
675
676                            case REDUCE_PRIORITY:
677                                // 3 is an adjustable number.
678                                Process.setThreadPriority(
679                                        Process.THREAD_PRIORITY_DEFAULT + 3 *
680                                        Process.THREAD_PRIORITY_LESS_FAVORABLE);
681                                break;
682
683                            case RESUME_PRIORITY:
684                                Process.setThreadPriority(
685                                        Process.THREAD_PRIORITY_DEFAULT);
686                                break;
687
688                            case EventHub.ADD_PACKAGE_NAME:
689                                if (BrowserFrame.sJavaBridge == null) {
690                                    throw new IllegalStateException(
691                                            "No WebView has been created in this process!");
692                                }
693                                BrowserFrame.sJavaBridge.addPackageName((String) msg.obj);
694                                break;
695
696                            case EventHub.REMOVE_PACKAGE_NAME:
697                                if (BrowserFrame.sJavaBridge == null) {
698                                    throw new IllegalStateException(
699                                            "No WebView has been created in this process!");
700                                }
701                                BrowserFrame.sJavaBridge.removePackageName((String) msg.obj);
702                                break;
703
704                            case EventHub.PROXY_CHANGED:
705                                if (BrowserFrame.sJavaBridge == null) {
706                                    throw new IllegalStateException(
707                                            "No WebView has been created in this process!");
708                                }
709                                BrowserFrame.sJavaBridge.updateProxy((String) msg.obj);
710                                break;
711                        }
712                    }
713                };
714                WebViewCore.class.notify();
715            }
716            Looper.loop();
717        }
718    }
719
720    static class BaseUrlData {
721        String mBaseUrl;
722        String mData;
723        String mMimeType;
724        String mEncoding;
725        String mHistoryUrl;
726    }
727
728    static class CursorData {
729        CursorData() {}
730        CursorData(int frame, int node, int x, int y) {
731            mFrame = frame;
732            mNode = node;
733            mX = x;
734            mY = y;
735        }
736        int mMoveGeneration;
737        int mFrame;
738        int mNode;
739        int mX;
740        int mY;
741    }
742
743    static class JSInterfaceData {
744        Object mObject;
745        String mInterfaceName;
746    }
747
748    static class JSKeyData {
749        String mCurrentText;
750        KeyEvent mEvent;
751    }
752
753    static class MotionUpData {
754        int mFrame;
755        int mNode;
756        Rect mBounds;
757        int mX;
758        int mY;
759    }
760
761    static class GetUrlData {
762        String mUrl;
763        Map<String, String> mExtraHeaders;
764    }
765
766    static class PostUrlData {
767        String mUrl;
768        byte[] mPostData;
769    }
770
771    static class ReplaceTextData {
772        String mReplace;
773        int mNewStart;
774        int mNewEnd;
775        int mTextGeneration;
776    }
777
778    static class TextSelectionData {
779        public TextSelectionData(int start, int end) {
780            mStart = start;
781            mEnd = end;
782        }
783        int mStart;
784        int mEnd;
785    }
786
787    static class TouchUpData {
788        int mMoveGeneration;
789        int mFrame;
790        int mNode;
791        int mX;
792        int mY;
793    }
794
795    static class TouchHighlightData {
796        int mX;
797        int mY;
798        int mSlop;
799    }
800
801    static class AutoFillData {
802        public AutoFillData() {
803            mQueryId = WebTextView.FORM_NOT_AUTOFILLABLE;
804            mPreview = "";
805        }
806
807        public AutoFillData(int queryId, String preview) {
808            mQueryId = queryId;
809            mPreview = preview;
810        }
811
812        public int getQueryId() {
813            return mQueryId;
814        }
815
816        public String getPreviewString() {
817            return mPreview;
818        }
819
820        private int mQueryId;
821        private String mPreview;
822    }
823
824    // mAction of TouchEventData can be MotionEvent.getAction() which uses the
825    // last two bytes or one of the following values
826    static final int ACTION_LONGPRESS = 0x100;
827    static final int ACTION_DOUBLETAP = 0x200;
828
829    static class TouchEventData {
830        int mAction;
831        int[] mIds;  // Ids of the touch points
832        Point[] mPoints;
833        int mMetaState;
834        boolean mReprocess;
835        MotionEvent mMotionEvent;
836    }
837
838    static class GeolocationPermissionsData {
839        String mOrigin;
840        boolean mAllow;
841        boolean mRemember;
842    }
843
844        static final String[] HandlerDebugString = {
845            "REVEAL_SELECTION", // 96
846            "REQUEST_LABEL", // 97
847            "UPDATE_FRAME_CACHE_IF_LOADING", // = 98
848            "SCROLL_TEXT_INPUT", // = 99
849            "LOAD_URL", // = 100;
850            "STOP_LOADING", // = 101;
851            "RELOAD", // = 102;
852            "KEY_DOWN", // = 103;
853            "KEY_UP", // = 104;
854            "VIEW_SIZE_CHANGED", // = 105;
855            "GO_BACK_FORWARD", // = 106;
856            "SET_SCROLL_OFFSET", // = 107;
857            "RESTORE_STATE", // = 108;
858            "PAUSE_TIMERS", // = 109;
859            "RESUME_TIMERS", // = 110;
860            "CLEAR_CACHE", // = 111;
861            "CLEAR_HISTORY", // = 112;
862            "SET_SELECTION", // = 113;
863            "REPLACE_TEXT", // = 114;
864            "PASS_TO_JS", // = 115;
865            "SET_GLOBAL_BOUNDS", // = 116;
866            "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117;
867            "CLICK", // = 118;
868            "SET_NETWORK_STATE", // = 119;
869            "DOC_HAS_IMAGES", // = 120;
870            "FAKE_CLICK", // = 121;
871            "DELETE_SELECTION", // = 122;
872            "LISTBOX_CHOICES", // = 123;
873            "SINGLE_LISTBOX_CHOICE", // = 124;
874            "MESSAGE_RELAY", // = 125;
875            "SET_BACKGROUND_COLOR", // = 126;
876            "SET_MOVE_FOCUS", // = 127
877            "SAVE_DOCUMENT_STATE", // = 128;
878            "129", // = 129;
879            "WEBKIT_DRAW", // = 130;
880            "SYNC_SCROLL", // = 131;
881            "POST_URL", // = 132;
882            "SPLIT_PICTURE_SET", // = 133;
883            "CLEAR_CONTENT", // = 134;
884            "SET_MOVE_MOUSE", // = 135;
885            "SET_MOVE_MOUSE_IF_LATEST", // = 136;
886            "REQUEST_CURSOR_HREF", // = 137;
887            "ADD_JS_INTERFACE", // = 138;
888            "LOAD_DATA", // = 139;
889            "TOUCH_UP", // = 140;
890            "TOUCH_EVENT", // = 141;
891            "SET_ACTIVE", // = 142;
892            "ON_PAUSE",     // = 143
893            "ON_RESUME",    // = 144
894            "FREE_MEMORY",  // = 145
895            "VALID_NODE_BOUNDS", // = 146
896            "SAVE_WEBARCHIVE", // = 147
897            "WEBKIT_DRAW_LAYERS", // = 148;
898            "REMOVE_JS_INTERFACE", // = 149;
899        };
900
901    class EventHub {
902        // Message Ids
903        static final int REVEAL_SELECTION = 96;
904        static final int REQUEST_LABEL = 97;
905        static final int UPDATE_FRAME_CACHE_IF_LOADING = 98;
906        static final int SCROLL_TEXT_INPUT = 99;
907        static final int LOAD_URL = 100;
908        static final int STOP_LOADING = 101;
909        static final int RELOAD = 102;
910        static final int KEY_DOWN = 103;
911        static final int KEY_UP = 104;
912        static final int VIEW_SIZE_CHANGED = 105;
913        static final int GO_BACK_FORWARD = 106;
914        static final int SET_SCROLL_OFFSET = 107;
915        static final int RESTORE_STATE = 108;
916        static final int PAUSE_TIMERS = 109;
917        static final int RESUME_TIMERS = 110;
918        static final int CLEAR_CACHE = 111;
919        static final int CLEAR_HISTORY = 112;
920        static final int SET_SELECTION = 113;
921        static final int REPLACE_TEXT = 114;
922        static final int PASS_TO_JS = 115;
923        static final int SET_GLOBAL_BOUNDS = 116;
924        static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117;
925        static final int CLICK = 118;
926        static final int SET_NETWORK_STATE = 119;
927        static final int DOC_HAS_IMAGES = 120;
928        static final int FAKE_CLICK = 121;
929        static final int DELETE_SELECTION = 122;
930        static final int LISTBOX_CHOICES = 123;
931        static final int SINGLE_LISTBOX_CHOICE = 124;
932        static final int MESSAGE_RELAY = 125;
933        static final int SET_BACKGROUND_COLOR = 126;
934        static final int SET_MOVE_FOCUS = 127;
935        static final int SAVE_DOCUMENT_STATE = 128;
936
937        static final int WEBKIT_DRAW = 130;
938        static final int SYNC_SCROLL = 131;
939        static final int POST_URL = 132;
940        static final int SPLIT_PICTURE_SET = 133;
941        static final int CLEAR_CONTENT = 134;
942
943        // UI nav messages
944        static final int SET_MOVE_MOUSE = 135;
945        static final int SET_MOVE_MOUSE_IF_LATEST = 136;
946        static final int REQUEST_CURSOR_HREF = 137;
947        static final int ADD_JS_INTERFACE = 138;
948        static final int LOAD_DATA = 139;
949
950        // motion
951        static final int TOUCH_UP = 140;
952        // message used to pass UI touch events to WebCore
953        static final int TOUCH_EVENT = 141;
954
955        // Used to tell the focus controller not to draw the blinking cursor,
956        // based on whether the WebView has focus and whether the WebView's
957        // cursor matches the webpage's focus.
958        static final int SET_ACTIVE = 142;
959
960        // lifecycle activities for just this DOM (unlike pauseTimers, which
961        // is global)
962        static final int ON_PAUSE = 143;
963        static final int ON_RESUME = 144;
964        static final int FREE_MEMORY = 145;
965        static final int VALID_NODE_BOUNDS = 146;
966
967        // Load and save web archives
968        static final int SAVE_WEBARCHIVE = 147;
969
970        // Update layers
971        static final int WEBKIT_DRAW_LAYERS = 148;
972
973        static final int REMOVE_JS_INTERFACE = 149;
974
975        // Network-based messaging
976        static final int CLEAR_SSL_PREF_TABLE = 150;
977
978        // Test harness messages
979        static final int REQUEST_EXT_REPRESENTATION = 160;
980        static final int REQUEST_DOC_AS_TEXT = 161;
981
982        // debugging
983        static final int DUMP_DOMTREE = 170;
984        static final int DUMP_RENDERTREE = 171;
985        static final int DUMP_NAVTREE = 172;
986        static final int DUMP_V8COUNTERS = 173;
987
988        static final int SET_JS_FLAGS = 174;
989        // Geolocation
990        static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180;
991
992        static final int POPULATE_VISITED_LINKS = 181;
993
994        static final int HIDE_FULLSCREEN = 182;
995
996        static final int SET_NETWORK_TYPE = 183;
997
998        // navigator.isApplicationInstalled()
999        static final int ADD_PACKAGE_NAMES = 184;
1000        static final int ADD_PACKAGE_NAME = 185;
1001        static final int REMOVE_PACKAGE_NAME = 186;
1002
1003        static final int GET_TOUCH_HIGHLIGHT_RECTS = 187;
1004        static final int REMOVE_TOUCH_HIGHLIGHT_RECTS = 188;
1005
1006        // accessibility support
1007        static final int MODIFY_SELECTION = 190;
1008
1009        static final int USE_MOCK_DEVICE_ORIENTATION = 191;
1010
1011        static final int AUTOFILL_FORM = 192;
1012
1013        static final int PROXY_CHANGED = 193;
1014
1015        // accessibility support
1016        static final int MOVE_SELECTION = 194;
1017
1018        // private message ids
1019        private static final int DESTROY =     200;
1020
1021        // Private handler for WebCore messages.
1022        private Handler mHandler;
1023        // Message queue for containing messages before the WebCore thread is
1024        // ready.
1025        private ArrayList<Message> mMessages = new ArrayList<Message>();
1026        // Flag for blocking messages. This is used during DESTROY to avoid
1027        // posting more messages to the EventHub or to WebView's event handler.
1028        private boolean mBlockMessages;
1029
1030        private int mTid;
1031        private int mSavedPriority;
1032
1033        /**
1034         * Prevent other classes from creating an EventHub.
1035         */
1036        private EventHub() {}
1037
1038        private static final int FIRST_PACKAGE_MSG_ID = REVEAL_SELECTION;
1039        private static final int LAST_PACKAGE_MSG_ID = VALID_NODE_BOUNDS;
1040
1041        /**
1042         * Transfer all messages to the newly created webcore thread handler.
1043         */
1044        private void transferMessages() {
1045            mTid = Process.myTid();
1046            mSavedPriority = Process.getThreadPriority(mTid);
1047
1048            mHandler = new Handler() {
1049                @Override
1050                public void handleMessage(Message msg) {
1051                    if (DebugFlags.WEB_VIEW_CORE) {
1052                        Log.v(LOGTAG, (msg.what < FIRST_PACKAGE_MSG_ID
1053                                || msg.what > LAST_PACKAGE_MSG_ID
1054                                ? Integer.toString(msg.what)
1055                                : HandlerDebugString[msg.what
1056                                        - FIRST_PACKAGE_MSG_ID])
1057                                + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
1058                                + " obj=" + msg.obj);
1059                    }
1060                    switch (msg.what) {
1061                        case WEBKIT_DRAW:
1062                            webkitDraw();
1063                            break;
1064
1065                        case WEBKIT_DRAW_LAYERS:
1066                            webkitDrawLayers();
1067                            break;
1068
1069                        case DESTROY:
1070                            // Time to take down the world. Cancel all pending
1071                            // loads and destroy the native view and frame.
1072                            synchronized (WebViewCore.this) {
1073                                mBrowserFrame.destroy();
1074                                mBrowserFrame = null;
1075                                mSettings.onDestroyed();
1076                                mNativeClass = 0;
1077                                mWebView = null;
1078                            }
1079                            break;
1080
1081                        case REVEAL_SELECTION:
1082                            nativeRevealSelection();
1083                            break;
1084
1085                        case REQUEST_LABEL:
1086                            if (mWebView != null) {
1087                                int nodePointer = msg.arg2;
1088                                String label = nativeRequestLabel(msg.arg1,
1089                                        nodePointer);
1090                                if (label != null && label.length() > 0) {
1091                                    Message.obtain(mWebView.mPrivateHandler,
1092                                            WebView.RETURN_LABEL, nodePointer,
1093                                            0, label).sendToTarget();
1094                                }
1095                            }
1096                            break;
1097
1098                        case UPDATE_FRAME_CACHE_IF_LOADING:
1099                            nativeUpdateFrameCacheIfLoading();
1100                            break;
1101
1102                        case SCROLL_TEXT_INPUT:
1103                            nativeScrollFocusedTextInput(
1104                                    ((Float) msg.obj).floatValue(), msg.arg1);
1105                            break;
1106
1107                        case LOAD_URL: {
1108                            CookieManager.getInstance().waitForCookieOperationsToComplete();
1109                            GetUrlData param = (GetUrlData) msg.obj;
1110                            loadUrl(param.mUrl, param.mExtraHeaders);
1111                            break;
1112                        }
1113
1114                        case POST_URL: {
1115                            CookieManager.getInstance().waitForCookieOperationsToComplete();
1116                            PostUrlData param = (PostUrlData) msg.obj;
1117                            mBrowserFrame.postUrl(param.mUrl, param.mPostData);
1118                            break;
1119                        }
1120                        case LOAD_DATA:
1121                            CookieManager.getInstance().waitForCookieOperationsToComplete();
1122                            BaseUrlData loadParams = (BaseUrlData) msg.obj;
1123                            String baseUrl = loadParams.mBaseUrl;
1124                            if (baseUrl != null) {
1125                                int i = baseUrl.indexOf(':');
1126                                if (i > 0) {
1127                                    /*
1128                                     * In 1.0, {@link
1129                                     * WebView#loadDataWithBaseURL} can access
1130                                     * local asset files as long as the data is
1131                                     * valid. In the new WebKit, the restriction
1132                                     * is tightened. To be compatible with 1.0,
1133                                     * we automatically add the scheme of the
1134                                     * baseUrl for local access as long as it is
1135                                     * not http(s)/ftp(s)/about/javascript
1136                                     */
1137                                    String scheme = baseUrl.substring(0, i);
1138                                    if (!scheme.startsWith("http") &&
1139                                            !scheme.startsWith("ftp") &&
1140                                            !scheme.startsWith("about") &&
1141                                            !scheme.startsWith("javascript")) {
1142                                        nativeRegisterURLSchemeAsLocal(scheme);
1143                                    }
1144                                }
1145                            }
1146                            mBrowserFrame.loadData(baseUrl,
1147                                    loadParams.mData,
1148                                    loadParams.mMimeType,
1149                                    loadParams.mEncoding,
1150                                    loadParams.mHistoryUrl);
1151                            break;
1152
1153                        case STOP_LOADING:
1154                            // If the WebCore has committed the load, but not
1155                            // finished the first layout yet, we need to set
1156                            // first layout done to trigger the interpreted side sync
1157                            // up with native side
1158                            if (mBrowserFrame.committed()
1159                                    && !mBrowserFrame.firstLayoutDone()) {
1160                                mBrowserFrame.didFirstLayout();
1161                            }
1162                            // Do this after syncing up the layout state.
1163                            stopLoading();
1164                            break;
1165
1166                        case RELOAD:
1167                            mBrowserFrame.reload(false);
1168                            break;
1169
1170                        case KEY_DOWN:
1171                            key((KeyEvent) msg.obj, true);
1172                            break;
1173
1174                        case KEY_UP:
1175                            key((KeyEvent) msg.obj, false);
1176                            break;
1177
1178                        case FAKE_CLICK:
1179                            nativeClick(msg.arg1, msg.arg2, true);
1180                            break;
1181
1182                        case CLICK:
1183                            nativeClick(msg.arg1, msg.arg2, false);
1184                            break;
1185
1186                        case VIEW_SIZE_CHANGED: {
1187                            WebView.ViewSizeData data =
1188                                    (WebView.ViewSizeData) msg.obj;
1189                            viewSizeChanged(data.mWidth, data.mHeight,
1190                                    data.mTextWrapWidth, data.mScale,
1191                                    data.mAnchorX, data.mAnchorY,
1192                                    data.mIgnoreHeight);
1193                            break;
1194                        }
1195                        case SET_SCROLL_OFFSET:
1196                            // note: these are in document coordinates
1197                            // (inv-zoom)
1198                            Point pt = (Point) msg.obj;
1199                            nativeSetScrollOffset(msg.arg1, msg.arg2, pt.x, pt.y);
1200                            break;
1201
1202                        case SET_GLOBAL_BOUNDS:
1203                            Rect r = (Rect) msg.obj;
1204                            nativeSetGlobalBounds(r.left, r.top, r.width(),
1205                                r.height());
1206                            break;
1207
1208                        case GO_BACK_FORWARD:
1209                            // If it is a standard load and the load is not
1210                            // committed yet, we interpret BACK as RELOAD
1211                            if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
1212                                    (mBrowserFrame.loadType() ==
1213                                    BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
1214                                mBrowserFrame.reload(true);
1215                            } else {
1216                                mBrowserFrame.goBackOrForward(msg.arg1);
1217                            }
1218                            break;
1219
1220                        case RESTORE_STATE:
1221                            stopLoading();
1222                            restoreState(msg.arg1);
1223                            break;
1224
1225                        case PAUSE_TIMERS:
1226                            mSavedPriority = Process.getThreadPriority(mTid);
1227                            Process.setThreadPriority(mTid,
1228                                    Process.THREAD_PRIORITY_BACKGROUND);
1229                            pauseTimers();
1230                            if (!JniUtil.useChromiumHttpStack()) {
1231                                WebViewWorker.getHandler().sendEmptyMessage(
1232                                        WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION);
1233                            }
1234                            break;
1235
1236                        case RESUME_TIMERS:
1237                            Process.setThreadPriority(mTid, mSavedPriority);
1238                            resumeTimers();
1239                            if (!JniUtil.useChromiumHttpStack()) {
1240                                WebViewWorker.getHandler().sendEmptyMessage(
1241                                        WebViewWorker.MSG_RESUME_CACHE_TRANSACTION);
1242                            }
1243                            break;
1244
1245                        case ON_PAUSE:
1246                            nativePause();
1247                            break;
1248
1249                        case ON_RESUME:
1250                            nativeResume();
1251                            break;
1252
1253                        case FREE_MEMORY:
1254                            clearCache(false);
1255                            nativeFreeMemory();
1256                            break;
1257
1258                        case SET_NETWORK_STATE:
1259                            if (BrowserFrame.sJavaBridge == null) {
1260                                throw new IllegalStateException("No WebView " +
1261                                        "has been created in this process!");
1262                            }
1263                            BrowserFrame.sJavaBridge
1264                                    .setNetworkOnLine(msg.arg1 == 1);
1265                            break;
1266
1267                        case SET_NETWORK_TYPE:
1268                            if (BrowserFrame.sJavaBridge == null) {
1269                                throw new IllegalStateException("No WebView " +
1270                                        "has been created in this process!");
1271                            }
1272                            Map<String, String> map = (Map<String, String>) msg.obj;
1273                            BrowserFrame.sJavaBridge
1274                                    .setNetworkType(map.get("type"), map.get("subtype"));
1275                            break;
1276
1277                        case CLEAR_CACHE:
1278                            clearCache(msg.arg1 == 1);
1279                            break;
1280
1281                        case CLEAR_HISTORY:
1282                            mCallbackProxy.getBackForwardList().
1283                                    close(mBrowserFrame.mNativeFrame);
1284                            break;
1285
1286                        case REPLACE_TEXT:
1287                            ReplaceTextData rep = (ReplaceTextData) msg.obj;
1288                            nativeReplaceTextfieldText(msg.arg1, msg.arg2,
1289                                    rep.mReplace, rep.mNewStart, rep.mNewEnd,
1290                                    rep.mTextGeneration);
1291                            break;
1292
1293                        case PASS_TO_JS: {
1294                            JSKeyData jsData = (JSKeyData) msg.obj;
1295                            KeyEvent evt = jsData.mEvent;
1296                            int keyCode = evt.getKeyCode();
1297                            int keyValue = evt.getUnicodeChar();
1298                            int generation = msg.arg1;
1299                            passToJs(generation,
1300                                    jsData.mCurrentText,
1301                                    keyCode,
1302                                    keyValue,
1303                                    evt.isDown(),
1304                                    evt.isShiftPressed(), evt.isAltPressed(),
1305                                    evt.isSymPressed());
1306                            break;
1307                        }
1308
1309                        case SAVE_DOCUMENT_STATE: {
1310                            CursorData cDat = (CursorData) msg.obj;
1311                            nativeSaveDocumentState(cDat.mFrame);
1312                            break;
1313                        }
1314
1315                        case CLEAR_SSL_PREF_TABLE:
1316                            Network.getInstance(mContext)
1317                                    .clearUserSslPrefTable();
1318                            break;
1319
1320                        case TOUCH_UP:
1321                            TouchUpData touchUpData = (TouchUpData) msg.obj;
1322                            nativeTouchUp(touchUpData.mMoveGeneration,
1323                                    touchUpData.mFrame, touchUpData.mNode,
1324                                    touchUpData.mX, touchUpData.mY);
1325                            break;
1326
1327                        case TOUCH_EVENT: {
1328                            TouchEventData ted = (TouchEventData) msg.obj;
1329                            final int count = ted.mPoints.length;
1330                            int[] xArray = new int[count];
1331                            int[] yArray = new int[count];
1332                            for (int c = 0; c < count; c++) {
1333                                xArray[c] = ted.mPoints[c].x;
1334                                yArray[c] = ted.mPoints[c].y;
1335                            }
1336                            Message.obtain(
1337                                    mWebView.mPrivateHandler,
1338                                    WebView.PREVENT_TOUCH_ID,
1339                                    ted.mAction,
1340                                    nativeHandleTouchEvent(ted.mAction, ted.mIds,
1341                                        xArray, yArray, count, ted.mMetaState) ? 1 : 0,
1342                                    ted.mReprocess ? ted : null).sendToTarget();
1343                            break;
1344                        }
1345
1346                        case SET_ACTIVE:
1347                            nativeSetFocusControllerActive(msg.arg1 == 1);
1348                            break;
1349
1350                        case ADD_JS_INTERFACE:
1351                            JSInterfaceData jsData = (JSInterfaceData) msg.obj;
1352                            mBrowserFrame.addJavascriptInterface(jsData.mObject,
1353                                    jsData.mInterfaceName);
1354                            break;
1355
1356                        case REMOVE_JS_INTERFACE:
1357                            jsData = (JSInterfaceData) msg.obj;
1358                            mBrowserFrame.removeJavascriptInterface(
1359                                    jsData.mInterfaceName);
1360                            break;
1361
1362                        case REQUEST_EXT_REPRESENTATION:
1363                            mBrowserFrame.externalRepresentation(
1364                                    (Message) msg.obj);
1365                            break;
1366
1367                        case REQUEST_DOC_AS_TEXT:
1368                            mBrowserFrame.documentAsText((Message) msg.obj);
1369                            break;
1370
1371                        case SET_MOVE_FOCUS:
1372                            CursorData focusData = (CursorData) msg.obj;
1373                            nativeMoveFocus(focusData.mFrame, focusData.mNode);
1374                            break;
1375
1376                        case SET_MOVE_MOUSE:
1377                            CursorData cursorData = (CursorData) msg.obj;
1378                            nativeMoveMouse(cursorData.mFrame,
1379                                     cursorData.mX, cursorData.mY);
1380                            break;
1381
1382                        case SET_MOVE_MOUSE_IF_LATEST:
1383                            CursorData cData = (CursorData) msg.obj;
1384                            nativeMoveMouseIfLatest(cData.mMoveGeneration,
1385                                    cData.mFrame,
1386                                    cData.mX, cData.mY);
1387                            break;
1388
1389                        case REQUEST_CURSOR_HREF: {
1390                            Message hrefMsg = (Message) msg.obj;
1391                            hrefMsg.getData().putString("url",
1392                                    nativeRetrieveHref(msg.arg1, msg.arg2));
1393                            hrefMsg.getData().putString("title",
1394                                    nativeRetrieveAnchorText(msg.arg1, msg.arg2));
1395                            hrefMsg.getData().putString("src",
1396                                    nativeRetrieveImageSource(msg.arg1, msg.arg2));
1397                            hrefMsg.sendToTarget();
1398                            break;
1399                        }
1400
1401                        case UPDATE_CACHE_AND_TEXT_ENTRY:
1402                            nativeUpdateFrameCache();
1403                            // FIXME: this should provide a minimal rectangle
1404                            if (mWebView != null) {
1405                                mWebView.postInvalidate();
1406                            }
1407                            sendUpdateTextEntry();
1408                            break;
1409
1410                        case DOC_HAS_IMAGES:
1411                            Message imageResult = (Message) msg.obj;
1412                            imageResult.arg1 =
1413                                    mBrowserFrame.documentHasImages() ? 1 : 0;
1414                            imageResult.sendToTarget();
1415                            break;
1416
1417                        case DELETE_SELECTION:
1418                            TextSelectionData deleteSelectionData
1419                                    = (TextSelectionData) msg.obj;
1420                            nativeDeleteSelection(deleteSelectionData.mStart,
1421                                    deleteSelectionData.mEnd, msg.arg1);
1422                            break;
1423
1424                        case SET_SELECTION:
1425                            nativeSetSelection(msg.arg1, msg.arg2);
1426                            break;
1427
1428                        case MODIFY_SELECTION:
1429                            String modifiedSelectionString = nativeModifySelection(msg.arg1,
1430                                    msg.arg2);
1431                            mWebView.mPrivateHandler.obtainMessage(WebView.SELECTION_STRING_CHANGED,
1432                                    modifiedSelectionString).sendToTarget();
1433                            break;
1434
1435                        case MOVE_SELECTION:
1436                            String movedSelectionString = nativeMoveSelection(msg.arg1, msg.arg2);
1437                            mWebView.mPrivateHandler.obtainMessage(WebView.SELECTION_STRING_CHANGED,
1438                                    movedSelectionString).sendToTarget();
1439                            break;
1440
1441                        case LISTBOX_CHOICES:
1442                            SparseBooleanArray choices = (SparseBooleanArray)
1443                                    msg.obj;
1444                            int choicesSize = msg.arg1;
1445                            boolean[] choicesArray = new boolean[choicesSize];
1446                            for (int c = 0; c < choicesSize; c++) {
1447                                choicesArray[c] = choices.get(c);
1448                            }
1449                            nativeSendListBoxChoices(choicesArray,
1450                                    choicesSize);
1451                            break;
1452
1453                        case SINGLE_LISTBOX_CHOICE:
1454                            nativeSendListBoxChoice(msg.arg1);
1455                            break;
1456
1457                        case SET_BACKGROUND_COLOR:
1458                            nativeSetBackgroundColor(msg.arg1);
1459                            break;
1460
1461                        case DUMP_DOMTREE:
1462                            nativeDumpDomTree(msg.arg1 == 1);
1463                            break;
1464
1465                        case DUMP_RENDERTREE:
1466                            nativeDumpRenderTree(msg.arg1 == 1);
1467                            break;
1468
1469                        case DUMP_NAVTREE:
1470                            nativeDumpNavTree();
1471                            break;
1472
1473                        case DUMP_V8COUNTERS:
1474                            nativeDumpV8Counters();
1475                            break;
1476
1477                        case SET_JS_FLAGS:
1478                            nativeSetJsFlags((String)msg.obj);
1479                            break;
1480
1481                        case SAVE_WEBARCHIVE:
1482                            WebView.SaveWebArchiveMessage saveMessage =
1483                                (WebView.SaveWebArchiveMessage)msg.obj;
1484                            saveMessage.mResultFile =
1485                                saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname);
1486                            mWebView.mPrivateHandler.obtainMessage(
1487                                WebView.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget();
1488                            break;
1489
1490                        case GEOLOCATION_PERMISSIONS_PROVIDE:
1491                            GeolocationPermissionsData data =
1492                                    (GeolocationPermissionsData) msg.obj;
1493                            nativeGeolocationPermissionsProvide(data.mOrigin,
1494                                    data.mAllow, data.mRemember);
1495                            break;
1496
1497                        case SYNC_SCROLL:
1498                            mWebkitScrollX = msg.arg1;
1499                            mWebkitScrollY = msg.arg2;
1500                            break;
1501
1502                        case SPLIT_PICTURE_SET:
1503                            nativeSplitContent(msg.arg1);
1504                            mWebView.mPrivateHandler.obtainMessage(
1505                                    WebView.REPLACE_BASE_CONTENT, msg.arg1, 0);
1506                            mSplitPictureIsScheduled = false;
1507                            break;
1508
1509                        case CLEAR_CONTENT:
1510                            // Clear the view so that onDraw() will draw nothing
1511                            // but white background
1512                            // (See public method WebView.clearView)
1513                            nativeClearContent();
1514                            break;
1515
1516                        case MESSAGE_RELAY:
1517                            if (msg.obj instanceof Message) {
1518                                ((Message) msg.obj).sendToTarget();
1519                            }
1520                            break;
1521
1522                        case POPULATE_VISITED_LINKS:
1523                            nativeProvideVisitedHistory((String[])msg.obj);
1524                            break;
1525
1526                        case VALID_NODE_BOUNDS: {
1527                            MotionUpData motionUpData = (MotionUpData) msg.obj;
1528                            if (!nativeValidNodeAndBounds(
1529                                    motionUpData.mFrame, motionUpData.mNode,
1530                                    motionUpData.mBounds)) {
1531                                nativeUpdateFrameCache();
1532                            }
1533                            Message message = mWebView.mPrivateHandler
1534                                    .obtainMessage(WebView.DO_MOTION_UP,
1535                                    motionUpData.mX, motionUpData.mY);
1536                            mWebView.mPrivateHandler.sendMessageAtFrontOfQueue(
1537                                    message);
1538                            break;
1539                        }
1540
1541                        case HIDE_FULLSCREEN:
1542                            nativeFullScreenPluginHidden(msg.arg1);
1543                            break;
1544
1545                        case ADD_PACKAGE_NAMES:
1546                            if (BrowserFrame.sJavaBridge == null) {
1547                                throw new IllegalStateException("No WebView " +
1548                                        "has been created in this process!");
1549                            }
1550                            BrowserFrame.sJavaBridge.addPackageNames(
1551                                    (Set<String>) msg.obj);
1552                            break;
1553
1554                        case GET_TOUCH_HIGHLIGHT_RECTS:
1555                            TouchHighlightData d = (TouchHighlightData) msg.obj;
1556                            ArrayList<Rect> rects = nativeGetTouchHighlightRects
1557                                    (d.mX, d.mY, d.mSlop);
1558                            mWebView.mPrivateHandler.obtainMessage(
1559                                    WebView.SET_TOUCH_HIGHLIGHT_RECTS, rects)
1560                                    .sendToTarget();
1561                            break;
1562
1563                        case REMOVE_TOUCH_HIGHLIGHT_RECTS:
1564                            mWebView.mPrivateHandler.obtainMessage(
1565                                    WebView.SET_TOUCH_HIGHLIGHT_RECTS, null)
1566                                    .sendToTarget();
1567                            break;
1568
1569                        case USE_MOCK_DEVICE_ORIENTATION:
1570                            useMockDeviceOrientation();
1571                            break;
1572
1573                        case AUTOFILL_FORM:
1574                            nativeAutoFillForm(msg.arg1);
1575                            mWebView.mPrivateHandler.obtainMessage(WebView.AUTOFILL_COMPLETE, null)
1576                                    .sendToTarget();
1577                            break;
1578                    }
1579                }
1580            };
1581            // Take all queued messages and resend them to the new handler.
1582            synchronized (this) {
1583                int size = mMessages.size();
1584                for (int i = 0; i < size; i++) {
1585                    mHandler.sendMessage(mMessages.get(i));
1586                }
1587                mMessages = null;
1588            }
1589        }
1590
1591        /**
1592         * Send a message internally to the queue or to the handler
1593         */
1594        private synchronized void sendMessage(Message msg) {
1595            if (mBlockMessages) {
1596                return;
1597            }
1598            if (mMessages != null) {
1599                mMessages.add(msg);
1600            } else {
1601                mHandler.sendMessage(msg);
1602            }
1603        }
1604
1605        private synchronized void removeMessages(int what) {
1606            if (mBlockMessages) {
1607                return;
1608            }
1609            if (what == EventHub.WEBKIT_DRAW) {
1610                mDrawIsScheduled = false;
1611            }
1612            if (mMessages != null) {
1613                Log.w(LOGTAG, "Not supported in this case.");
1614            } else {
1615                mHandler.removeMessages(what);
1616            }
1617        }
1618
1619        private synchronized boolean hasMessages(int what) {
1620            if (mBlockMessages) {
1621                return false;
1622            }
1623            if (mMessages != null) {
1624                Log.w(LOGTAG, "hasMessages() is not supported in this case.");
1625                return false;
1626            } else {
1627                return mHandler.hasMessages(what);
1628            }
1629        }
1630
1631        private synchronized void sendMessageDelayed(Message msg, long delay) {
1632            if (mBlockMessages) {
1633                return;
1634            }
1635            mHandler.sendMessageDelayed(msg, delay);
1636        }
1637
1638        /**
1639         * Send a message internally to the front of the queue.
1640         */
1641        private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1642            if (mBlockMessages) {
1643                return;
1644            }
1645            if (mMessages != null) {
1646                mMessages.add(0, msg);
1647            } else {
1648                mHandler.sendMessageAtFrontOfQueue(msg);
1649            }
1650        }
1651
1652        /**
1653         * Remove all the messages.
1654         */
1655        private synchronized void removeMessages() {
1656            // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1657            mDrawIsScheduled = false;
1658            mSplitPictureIsScheduled = false;
1659            if (mMessages != null) {
1660                mMessages.clear();
1661            } else {
1662                mHandler.removeCallbacksAndMessages(null);
1663            }
1664        }
1665
1666        /**
1667         * Block sending messages to the EventHub.
1668         */
1669        private synchronized void blockMessages() {
1670            mBlockMessages = true;
1671        }
1672    }
1673
1674    //-------------------------------------------------------------------------
1675    // Methods called by host activity (in the same thread)
1676    //-------------------------------------------------------------------------
1677
1678    void stopLoading() {
1679        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1680        if (mBrowserFrame != null) {
1681            mBrowserFrame.stopLoading();
1682        }
1683    }
1684
1685    //-------------------------------------------------------------------------
1686    // Methods called by WebView
1687    // If it refers to local variable, it needs synchronized().
1688    // If it needs WebCore, it has to send message.
1689    //-------------------------------------------------------------------------
1690
1691    void sendMessage(Message msg) {
1692        mEventHub.sendMessage(msg);
1693    }
1694
1695    void sendMessage(int what) {
1696        mEventHub.sendMessage(Message.obtain(null, what));
1697    }
1698
1699    void sendMessage(int what, Object obj) {
1700        mEventHub.sendMessage(Message.obtain(null, what, obj));
1701    }
1702
1703    void sendMessage(int what, int arg1) {
1704        // just ignore the second argument (make it 0)
1705        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1706    }
1707
1708    void sendMessage(int what, int arg1, int arg2) {
1709        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1710    }
1711
1712    void sendMessage(int what, int arg1, Object obj) {
1713        // just ignore the second argument (make it 0)
1714        mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1715    }
1716
1717    void sendMessage(int what, int arg1, int arg2, Object obj) {
1718        mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1719    }
1720
1721    void sendMessageAtFrontOfQueue(int what, Object obj) {
1722        mEventHub.sendMessageAtFrontOfQueue(Message.obtain(
1723                null, what, obj));
1724    }
1725
1726    void sendMessageDelayed(int what, Object obj, long delay) {
1727        mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1728    }
1729
1730    void removeMessages(int what) {
1731        mEventHub.removeMessages(what);
1732    }
1733
1734    void removeMessages() {
1735        mEventHub.removeMessages();
1736    }
1737
1738    /**
1739     * Removes pending messages and trigger a DESTROY message to send to
1740     * WebCore.
1741     * Called from UI thread.
1742     */
1743    void destroy() {
1744        // We don't want anyone to post a message between removing pending
1745        // messages and sending the destroy message.
1746        synchronized (mEventHub) {
1747            // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
1748            // be preserved even the WebView is destroyed.
1749            // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
1750            boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
1751            boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
1752            mEventHub.removeMessages();
1753            mEventHub.sendMessageAtFrontOfQueue(
1754                    Message.obtain(null, EventHub.DESTROY));
1755            if (hasPause) {
1756                mEventHub.sendMessageAtFrontOfQueue(
1757                        Message.obtain(null, EventHub.PAUSE_TIMERS));
1758            }
1759            if (hasResume) {
1760                mEventHub.sendMessageAtFrontOfQueue(
1761                        Message.obtain(null, EventHub.RESUME_TIMERS));
1762            }
1763            mEventHub.blockMessages();
1764        }
1765    }
1766
1767    //-------------------------------------------------------------------------
1768    // WebViewCore private methods
1769    //-------------------------------------------------------------------------
1770
1771    private void clearCache(boolean includeDiskFiles) {
1772        mBrowserFrame.clearCache();
1773        if (includeDiskFiles) {
1774            CacheManager.removeAllCacheFiles();
1775        }
1776    }
1777
1778    private void loadUrl(String url, Map<String, String> extraHeaders) {
1779        if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1780        mBrowserFrame.loadUrl(url, extraHeaders);
1781    }
1782
1783    private String saveWebArchive(String filename, boolean autoname) {
1784        if (DebugFlags.WEB_VIEW_CORE) {
1785            Log.v(LOGTAG, " CORE saveWebArchive " + filename + " " + autoname);
1786        }
1787        return mBrowserFrame.saveWebArchive(filename, autoname);
1788    }
1789
1790    private void key(KeyEvent evt, boolean isDown) {
1791        if (DebugFlags.WEB_VIEW_CORE) {
1792            Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1793                    + evt);
1794        }
1795        int keyCode = evt.getKeyCode();
1796        int unicodeChar = evt.getUnicodeChar();
1797
1798        if (keyCode == KeyEvent.KEYCODE_UNKNOWN && evt.getCharacters() != null
1799                && evt.getCharacters().length() > 0) {
1800            // we should only receive individual complex characters
1801            unicodeChar = evt.getCharacters().codePointAt(0);
1802        }
1803
1804        if (!nativeKey(keyCode, unicodeChar, evt.getRepeatCount(), evt.isShiftPressed(),
1805                evt.isAltPressed(), evt.isSymPressed(),
1806                isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1807            if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1808                    && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1809                if (DebugFlags.WEB_VIEW_CORE) {
1810                    Log.v(LOGTAG, "key: arrow unused by page: " + keyCode);
1811                }
1812                if (mWebView != null && evt.isDown()) {
1813                    Message.obtain(mWebView.mPrivateHandler,
1814                            WebView.UNHANDLED_NAV_KEY, keyCode,
1815                            0).sendToTarget();
1816                }
1817                return;
1818            }
1819            // bubble up the event handling
1820            // but do not bubble up the ENTER key, which would open the search
1821            // bar without any text.
1822            mCallbackProxy.onUnhandledKeyEvent(evt);
1823        }
1824    }
1825
1826    // These values are used to avoid requesting a layout based on old values
1827    private int mCurrentViewWidth = 0;
1828    private int mCurrentViewHeight = 0;
1829    private float mCurrentViewScale = 1.0f;
1830
1831    // notify webkit that our virtual view size changed size (after inv-zoom)
1832    private void viewSizeChanged(int w, int h, int textwrapWidth, float scale,
1833            int anchorX, int anchorY, boolean ignoreHeight) {
1834        if (DebugFlags.WEB_VIEW_CORE) {
1835            Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
1836                    + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
1837        }
1838        if (w == 0) {
1839            Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1840            return;
1841        }
1842        int width = w;
1843        if (mSettings.getUseWideViewPort()) {
1844            if (mViewportWidth == -1) {
1845                if (mSettings.getLayoutAlgorithm() ==
1846                        WebSettings.LayoutAlgorithm.NORMAL || mSettings.getUseFixedViewport()) {
1847                    width = WebView.DEFAULT_VIEWPORT_WIDTH;
1848                } else {
1849                    /*
1850                     * if a page's minimum preferred width is wider than the
1851                     * given "w", use it instead to get better layout result. If
1852                     * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1853                     * wider. If we start a page with screen width, due to the
1854                     * delay between {@link #didFirstLayout} and
1855                     * {@link #viewSizeChanged},
1856                     * {@link #nativeGetContentMinPrefWidth} will return a more
1857                     * accurate value than initial 0 to result a better layout.
1858                     * In the worse case, the native width will be adjusted when
1859                     * next zoom or screen orientation change happens.
1860                     */
1861                    width = Math.min(WebView.sMaxViewportWidth, Math.max(w,
1862                            Math.max(WebView.DEFAULT_VIEWPORT_WIDTH,
1863                                    nativeGetContentMinPrefWidth())));
1864                }
1865            } else if (mViewportWidth > 0) {
1866                if (mSettings.getUseFixedViewport()) {
1867                    // Use website specified or desired fixed viewport width.
1868                    width = mViewportWidth;
1869                } else {
1870                    width = Math.max(w, mViewportWidth);
1871                }
1872            } else if (mSettings.getUseFixedViewport()) {
1873                width = mWebView.getViewWidth();
1874            } else {
1875                width = textwrapWidth;
1876            }
1877        }
1878        nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
1879                textwrapWidth, scale, w, h, anchorX, anchorY, ignoreHeight);
1880        // Remember the current width and height
1881        boolean needInvalidate = (mCurrentViewWidth == 0);
1882        mCurrentViewWidth = w;
1883        mCurrentViewHeight = h;
1884        mCurrentViewScale = scale;
1885        if (needInvalidate) {
1886            // ensure {@link #webkitDraw} is called as we were blocking in
1887            // {@link #contentDraw} when mCurrentViewWidth is 0
1888            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1889            contentDraw();
1890        }
1891        mEventHub.sendMessage(Message.obtain(null,
1892                EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
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            if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
2014                // as we have the new picture, try to sync the scroll position
2015                Message.obtain(mWebView.mPrivateHandler,
2016                        WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
2017                        mWebkitScrollY).sendToTarget();
2018                mWebkitScrollX = mWebkitScrollY = 0;
2019            }
2020        }
2021    }
2022
2023    static void reducePriority() {
2024        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
2025        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
2026        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
2027        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
2028                .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
2029    }
2030
2031    static void resumePriority() {
2032        // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
2033        sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
2034        sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
2035        sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
2036                .obtainMessage(WebCoreThread.RESUME_PRIORITY));
2037    }
2038
2039    static void sendStaticMessage(int messageType, Object argument) {
2040        if (sWebCoreHandler == null)
2041            return;
2042
2043        sWebCoreHandler.sendMessage(sWebCoreHandler.obtainMessage(messageType, argument));
2044    }
2045
2046    static void pauseUpdatePicture(WebViewCore core) {
2047        // Note: there is one possible failure mode. If pauseUpdatePicture() is
2048        // called from UI thread while WEBKIT_DRAW is just pulled out of the
2049        // queue in WebCore thread to be executed. Then update won't be blocked.
2050        if (core != null) {
2051            if (!core.getSettings().enableSmoothTransition()) return;
2052
2053            synchronized (core) {
2054                core.mDrawIsPaused = true;
2055                if (core.mDrawIsScheduled) {
2056                    core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
2057                }
2058            }
2059        }
2060
2061    }
2062
2063    static void resumeUpdatePicture(WebViewCore core) {
2064        if (core != null) {
2065            // if mDrawIsPaused is true, ignore the setting, continue to resume
2066            if (!core.mDrawIsPaused
2067                    && !core.getSettings().enableSmoothTransition()) return;
2068
2069            synchronized (core) {
2070                core.mDrawIsPaused = false;
2071                // always redraw on resume to reenable gif animations
2072                core.mDrawIsScheduled = false;
2073                core.nativeContentInvalidateAll();
2074                core.contentDraw();
2075            }
2076        }
2077    }
2078
2079    //////////////////////////////////////////////////////////////////////////
2080
2081    private void restoreState(int index) {
2082        WebBackForwardList list = mCallbackProxy.getBackForwardList();
2083        int size = list.getSize();
2084        for (int i = 0; i < size; i++) {
2085            list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
2086        }
2087        mBrowserFrame.mLoadInitFromJava = true;
2088        list.restoreIndex(mBrowserFrame.mNativeFrame, index);
2089        mBrowserFrame.mLoadInitFromJava = false;
2090    }
2091
2092    //-------------------------------------------------------------------------
2093    // Implement abstract methods in WebViewCore, native WebKit callback part
2094    //-------------------------------------------------------------------------
2095
2096    // called from JNI or WebView thread
2097    /* package */ void contentDraw() {
2098        // don't update the Picture until we have an initial width and finish
2099        // the first layout
2100        if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
2101            return;
2102        }
2103        // only fire an event if this is our first request
2104        synchronized (this) {
2105            if (mDrawIsScheduled) return;
2106            mDrawIsScheduled = true;
2107            if (mDrawIsPaused) return;
2108            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
2109        }
2110    }
2111
2112    // called from JNI
2113    void layersDraw() {
2114        synchronized (this) {
2115            if (mDrawLayersIsScheduled) return;
2116            mDrawLayersIsScheduled = true;
2117            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW_LAYERS));
2118        }
2119    }
2120
2121    // called by JNI
2122    private void contentScrollBy(int dx, int dy, boolean animate) {
2123        if (!mBrowserFrame.firstLayoutDone()) {
2124            // Will this happen? If yes, we need to do something here.
2125            return;
2126        }
2127        if (mWebView != null) {
2128            Message msg = Message.obtain(mWebView.mPrivateHandler,
2129                    WebView.SCROLL_BY_MSG_ID, dx, dy, Boolean.valueOf(animate));
2130            if (mDrawIsScheduled) {
2131                mEventHub.sendMessage(Message.obtain(null,
2132                        EventHub.MESSAGE_RELAY, msg));
2133            } else {
2134                msg.sendToTarget();
2135            }
2136        }
2137    }
2138
2139    // called by JNI
2140    private void contentScrollTo(int x, int y) {
2141        if (!mBrowserFrame.firstLayoutDone()) {
2142            /*
2143             * WebKit restore state will be called before didFirstLayout(),
2144             * remember the position as it has to be applied after restoring
2145             * zoom factor which is controlled by screenWidth.
2146             */
2147            mRestoredX = x;
2148            mRestoredY = y;
2149            return;
2150        }
2151        if (mWebView != null) {
2152            Message msg = Message.obtain(mWebView.mPrivateHandler,
2153                    WebView.SCROLL_TO_MSG_ID, x, y);
2154            if (mDrawIsScheduled) {
2155                mEventHub.sendMessage(Message.obtain(null,
2156                        EventHub.MESSAGE_RELAY, msg));
2157            } else {
2158                msg.sendToTarget();
2159            }
2160        }
2161    }
2162
2163    // called by JNI
2164    private void contentSpawnScrollTo(int x, int y) {
2165        if (!mBrowserFrame.firstLayoutDone()) {
2166            /*
2167             * WebKit restore state will be called before didFirstLayout(),
2168             * remember the position as it has to be applied after restoring
2169             * zoom factor which is controlled by screenWidth.
2170             */
2171            mRestoredX = x;
2172            mRestoredY = y;
2173            return;
2174        }
2175        if (mWebView != null) {
2176            Message msg = Message.obtain(mWebView.mPrivateHandler,
2177                    WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
2178            if (mDrawIsScheduled) {
2179                mEventHub.sendMessage(Message.obtain(null,
2180                        EventHub.MESSAGE_RELAY, msg));
2181            } else {
2182                msg.sendToTarget();
2183            }
2184        }
2185    }
2186
2187    // called by JNI
2188    private void sendNotifyProgressFinished() {
2189        sendUpdateTextEntry();
2190        if (!JniUtil.useChromiumHttpStack()) {
2191            // as CacheManager can behave based on database transaction, we need to
2192            // call tick() to trigger endTransaction
2193            WebViewWorker.getHandler().removeMessages(
2194                    WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
2195            WebViewWorker.getHandler().sendEmptyMessage(
2196                    WebViewWorker.MSG_CACHE_TRANSACTION_TICKER);
2197        }
2198        contentDraw();
2199    }
2200
2201    /*  Called by JNI. The coordinates are in doc coordinates, so they need to
2202        be scaled before they can be used by the view system, which happens
2203        in WebView since it (and its thread) know the current scale factor.
2204     */
2205    private void sendViewInvalidate(int left, int top, int right, int bottom) {
2206        if (mWebView != null) {
2207            Message.obtain(mWebView.mPrivateHandler,
2208                           WebView.INVAL_RECT_MSG_ID,
2209                           new Rect(left, top, right, bottom)).sendToTarget();
2210        }
2211    }
2212
2213    private static boolean mRepaintScheduled = false;
2214
2215    /*
2216     * Called by the WebView thread
2217     */
2218    /* package */ void signalRepaintDone() {
2219        mRepaintScheduled = false;
2220    }
2221
2222    /* package */ WebView getWebView() {
2223        return mWebView;
2224    }
2225
2226    private native void setViewportSettingsFromNative();
2227
2228    // called by JNI
2229    private void didFirstLayout(boolean standardLoad) {
2230        if (DebugFlags.WEB_VIEW_CORE) {
2231            Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
2232        }
2233
2234        mBrowserFrame.didFirstLayout();
2235
2236        if (mWebView == null) return;
2237
2238        boolean updateViewState = standardLoad || mRestoredScale > 0;
2239        setupViewport(updateViewState);
2240        // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will
2241        // be called after the WebView updates its state. If updateRestoreState
2242        // is false, start to draw now as it is ready.
2243        if (!updateViewState) {
2244            mWebView.mViewManager.postReadyToDrawAll();
2245        }
2246
2247        // remove the touch highlight when moving to a new page
2248        if (getSettings().supportTouchOnly()) {
2249            mEventHub.sendMessage(Message.obtain(null,
2250                    EventHub.REMOVE_TOUCH_HIGHLIGHT_RECTS));
2251        }
2252
2253        // reset the scroll position, the restored offset and scales
2254        mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY = 0;
2255        mRestoredScale = mRestoredTextWrapScale = 0;
2256    }
2257
2258    // called by JNI
2259    private void updateViewport() {
2260        // if updateViewport is called before first layout, wait until first
2261        // layout to update the viewport. In the rare case, this is called after
2262        // first layout, force an update as we have just parsed the viewport
2263        // meta tag.
2264        if (mBrowserFrame.firstLayoutDone()) {
2265            setupViewport(true);
2266        }
2267    }
2268
2269    private void setupViewport(boolean updateViewState) {
2270        // set the viewport settings from WebKit
2271        setViewportSettingsFromNative();
2272
2273        // adjust the default scale to match the densityDpi
2274        float adjust = 1.0f;
2275        if (mViewportDensityDpi == -1) {
2276            // convert default zoom scale to a integer (percentage) to avoid any
2277            // issues with floating point comparisons
2278            if (mWebView != null && (int)(mWebView.getDefaultZoomScale() * 100) != 100) {
2279                adjust = mWebView.getDefaultZoomScale();
2280            }
2281        } else if (mViewportDensityDpi > 0) {
2282            adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
2283                    / mViewportDensityDpi;
2284        }
2285        int defaultScale = (int) (adjust * 100);
2286
2287        if (mViewportInitialScale > 0) {
2288            mViewportInitialScale *= adjust;
2289        }
2290        if (mViewportMinimumScale > 0) {
2291            mViewportMinimumScale *= adjust;
2292        }
2293        if (mViewportMaximumScale > 0) {
2294            mViewportMaximumScale *= adjust;
2295        }
2296
2297        // infer the values if they are not defined.
2298        if (mViewportWidth == 0) {
2299            if (mViewportInitialScale == 0) {
2300                mViewportInitialScale = defaultScale;
2301            }
2302        }
2303        if (mViewportUserScalable == false) {
2304            mViewportInitialScale = defaultScale;
2305            mViewportMinimumScale = defaultScale;
2306            mViewportMaximumScale = defaultScale;
2307        }
2308        if (mViewportMinimumScale > mViewportInitialScale
2309                && mViewportInitialScale != 0) {
2310            mViewportMinimumScale = mViewportInitialScale;
2311        }
2312        if (mViewportMaximumScale > 0
2313                && mViewportMaximumScale < mViewportInitialScale) {
2314            mViewportMaximumScale = mViewportInitialScale;
2315        }
2316        if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
2317            mViewportWidth = 0;
2318        }
2319
2320        // if mViewportWidth is 0, it means device-width, always update.
2321        if (mViewportWidth != 0 && !updateViewState) {
2322            ViewState viewState = new ViewState();
2323            viewState.mMinScale = mViewportMinimumScale / 100.0f;
2324            viewState.mMaxScale = mViewportMaximumScale / 100.0f;
2325            viewState.mDefaultScale = adjust;
2326            // as mViewportWidth is not 0, it is not mobile site.
2327            viewState.mMobileSite = false;
2328            // for non-mobile site, we don't need minPrefWidth, set it as 0
2329            viewState.mScrollX = 0;
2330            Message.obtain(mWebView.mPrivateHandler,
2331                    WebView.UPDATE_ZOOM_RANGE, viewState).sendToTarget();
2332            return;
2333        }
2334
2335        // now notify webview
2336        // webViewWidth refers to the width in the view system
2337        int webViewWidth;
2338        // viewportWidth refers to the width in the document system
2339        int viewportWidth = mCurrentViewWidth;
2340        if (viewportWidth == 0) {
2341            // this may happen when WebView just starts. This is not perfect as
2342            // we call WebView method from WebCore thread. But not perfect
2343            // reference is better than no reference.
2344            webViewWidth = mWebView.getViewWidth();
2345            viewportWidth = (int) (webViewWidth / adjust);
2346            if (viewportWidth == 0) {
2347                Log.w(LOGTAG, "Can't get the viewWidth after the first layout");
2348            }
2349        } else {
2350            webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
2351        }
2352        mInitialViewState = new ViewState();
2353        mInitialViewState.mMinScale = mViewportMinimumScale / 100.0f;
2354        mInitialViewState.mMaxScale = mViewportMaximumScale / 100.0f;
2355        mInitialViewState.mDefaultScale = adjust;
2356        mInitialViewState.mScrollX = mRestoredX;
2357        mInitialViewState.mScrollY = mRestoredY;
2358        mInitialViewState.mMobileSite = (0 == mViewportWidth);
2359        if (mRestoredScale > 0) {
2360            mInitialViewState.mIsRestored = true;
2361            mInitialViewState.mViewScale = mRestoredScale;
2362            if (mRestoredTextWrapScale > 0) {
2363                mInitialViewState.mTextWrapScale = mRestoredTextWrapScale;
2364            } else {
2365                mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale;
2366            }
2367        } else {
2368            if (mViewportInitialScale > 0) {
2369                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
2370                        mViewportInitialScale / 100.0f;
2371            } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth &&
2372                !mWebView.getSettings().getUseFixedViewport()) {
2373                mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale =
2374                        (float) webViewWidth / mViewportWidth;
2375            } else {
2376                mInitialViewState.mTextWrapScale = adjust;
2377                // 0 will trigger WebView to turn on zoom overview mode
2378                mInitialViewState.mViewScale = 0;
2379            }
2380        }
2381
2382        if (mWebView.mHeightCanMeasure) {
2383            // Trick to ensure that the Picture has the exact height for the
2384            // content by forcing to layout with 0 height after the page is
2385            // ready, which is indicated by didFirstLayout. This is essential to
2386            // get rid of the white space in the GMail which uses WebView for
2387            // message view.
2388            mWebView.mLastHeightSent = 0;
2389            // Send a negative scale to indicate that WebCore should reuse
2390            // the current scale
2391            WebView.ViewSizeData data = new WebView.ViewSizeData();
2392            data.mWidth = mWebView.mLastWidthSent;
2393            data.mHeight = 0;
2394            // if mHeightCanMeasure is true, getUseWideViewPort() can't be
2395            // true. It is safe to use mWidth for mTextWrapWidth.
2396            data.mTextWrapWidth = data.mWidth;
2397            data.mScale = -1.0f;
2398            data.mIgnoreHeight = false;
2399            data.mAnchorX = data.mAnchorY = 0;
2400            // send VIEW_SIZE_CHANGED to the front of the queue so that we can
2401            // avoid pushing the wrong picture to the WebView side. If there is
2402            // a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
2403            // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED
2404            // in the queue, as mLastHeightSent has been updated here, we may
2405            // miss the requestLayout in WebView side after the new picture.
2406            mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2407            mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2408                    EventHub.VIEW_SIZE_CHANGED, data));
2409        } else if (mSettings.getUseWideViewPort()) {
2410            if (viewportWidth == 0) {
2411                // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
2412                // to WebViewCore
2413                mWebView.mLastWidthSent = 0;
2414            } else {
2415                WebView.ViewSizeData data = new WebView.ViewSizeData();
2416                // mViewScale as 0 means it is in zoom overview mode. So we don't
2417                // know the exact scale. If mRestoredScale is non-zero, use it;
2418                // otherwise just use mTextWrapScale as the initial scale.
2419                data.mScale = mInitialViewState.mViewScale == 0
2420                        ? (mRestoredScale > 0 ? mRestoredScale
2421                                : mInitialViewState.mTextWrapScale)
2422                        : mInitialViewState.mViewScale;
2423                if (DebugFlags.WEB_VIEW_CORE) {
2424                    Log.v(LOGTAG, "setupViewport"
2425                             + " mRestoredScale=" + mRestoredScale
2426                             + " mViewScale=" + mInitialViewState.mViewScale
2427                             + " mTextWrapScale=" + mInitialViewState.mTextWrapScale
2428                             );
2429                }
2430                data.mWidth = Math.round(webViewWidth / data.mScale);
2431                // We may get a call here when mCurrentViewHeight == 0 if webcore completes the
2432                // first layout before we sync our webview dimensions to it. In that case, we
2433                // request the real height of the webview. This is not a perfect solution as we
2434                // are calling a WebView method from the WebCore thread. But this is preferable
2435                // to syncing an incorrect height.
2436                data.mHeight = mCurrentViewHeight == 0 ?
2437                        Math.round(mWebView.getViewHeight() / data.mScale)
2438                        : mCurrentViewHeight * data.mWidth / viewportWidth;
2439                data.mTextWrapWidth = Math.round(webViewWidth
2440                        / mInitialViewState.mTextWrapScale);
2441                data.mIgnoreHeight = false;
2442                data.mAnchorX = data.mAnchorY = 0;
2443                // send VIEW_SIZE_CHANGED to the front of the queue so that we
2444                // can avoid pushing the wrong picture to the WebView side.
2445                mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2446                mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2447                        EventHub.VIEW_SIZE_CHANGED, data));
2448            }
2449        }
2450    }
2451
2452    // called by JNI
2453    private void restoreScale(float scale, float textWrapScale) {
2454        if (mBrowserFrame.firstLayoutDone() == false) {
2455            mRestoredScale = scale;
2456            if (mSettings.getUseWideViewPort()) {
2457                mRestoredTextWrapScale = textWrapScale;
2458            }
2459        }
2460    }
2461
2462    // called by JNI
2463    private void needTouchEvents(boolean need) {
2464        if (mWebView != null) {
2465            Message.obtain(mWebView.mPrivateHandler,
2466                    WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
2467                    .sendToTarget();
2468        }
2469    }
2470
2471    // called by JNI
2472    private void updateTextfield(int ptr, boolean changeToPassword,
2473            String text, int textGeneration) {
2474        if (mWebView != null) {
2475            Message msg = Message.obtain(mWebView.mPrivateHandler,
2476                    WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
2477                    textGeneration, text);
2478            msg.getData().putBoolean("password", changeToPassword);
2479            msg.sendToTarget();
2480        }
2481    }
2482
2483    // called by JNI
2484    private void updateTextSelection(int pointer, int start, int end,
2485            int textGeneration) {
2486        if (mWebView != null) {
2487            Message.obtain(mWebView.mPrivateHandler,
2488                WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
2489                new TextSelectionData(start, end)).sendToTarget();
2490        }
2491    }
2492
2493    // called by JNI
2494    private void clearTextEntry() {
2495        if (mWebView == null) return;
2496        Message.obtain(mWebView.mPrivateHandler,
2497                WebView.CLEAR_TEXT_ENTRY).sendToTarget();
2498    }
2499
2500    // called by JNI
2501    private void sendFindAgain() {
2502        if (mWebView == null) return;
2503        Message.obtain(mWebView.mPrivateHandler,
2504                WebView.FIND_AGAIN).sendToTarget();
2505    }
2506
2507    private native void nativeUpdateFrameCacheIfLoading();
2508    private native void nativeRevealSelection();
2509    private native String nativeRequestLabel(int framePtr, int nodePtr);
2510    /**
2511     * Scroll the focused textfield to (xPercent, y) in document space
2512     */
2513    private native void nativeScrollFocusedTextInput(float xPercent, int y);
2514
2515    // these must be in document space (i.e. not scaled/zoomed).
2516    private native void nativeSetScrollOffset(int gen, int userScrolled, int dx, int dy);
2517
2518    private native void nativeSetGlobalBounds(int x, int y, int w, int h);
2519
2520    // called by JNI
2521    private void requestListBox(String[] array, int[] enabledArray,
2522            int[] selectedArray) {
2523        if (mWebView != null) {
2524            mWebView.requestListBox(array, enabledArray, selectedArray);
2525        }
2526    }
2527
2528    // called by JNI
2529    private void requestListBox(String[] array, int[] enabledArray,
2530            int selection) {
2531        if (mWebView != null) {
2532            mWebView.requestListBox(array, enabledArray, selection);
2533        }
2534
2535    }
2536
2537    // called by JNI
2538    private void requestKeyboardWithSelection(int pointer, int selStart,
2539            int selEnd, int textGeneration) {
2540        if (mWebView != null) {
2541            Message.obtain(mWebView.mPrivateHandler,
2542                    WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer,
2543                    textGeneration, new TextSelectionData(selStart, selEnd))
2544                    .sendToTarget();
2545        }
2546    }
2547
2548    // called by JNI
2549    private void requestKeyboard(boolean showKeyboard) {
2550        if (mWebView != null) {
2551            Message.obtain(mWebView.mPrivateHandler,
2552                    WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
2553                    .sendToTarget();
2554        }
2555    }
2556
2557    private void setWebTextViewAutoFillable(int queryId, String preview) {
2558        if (mWebView != null) {
2559            Message.obtain(mWebView.mPrivateHandler, WebView.SET_AUTOFILLABLE,
2560                    new AutoFillData(queryId, preview))
2561                    .sendToTarget();
2562        }
2563    }
2564
2565    // called by JNI
2566    private Context getContext() {
2567        return mContext;
2568    }
2569
2570    // called by JNI
2571    private Class<?> getPluginClass(String libName, String clsName) {
2572
2573        if (mWebView == null) {
2574            return null;
2575        }
2576
2577        PluginManager pluginManager = PluginManager.getInstance(null);
2578
2579        String pkgName = pluginManager.getPluginsAPKName(libName);
2580        if (pkgName == null) {
2581            Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2582            return null;
2583        }
2584
2585        try {
2586            return pluginManager.getPluginClass(pkgName, clsName);
2587        } catch (NameNotFoundException e) {
2588            Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")");
2589        } catch (ClassNotFoundException e) {
2590            Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
2591                    ") in the apk (" + pkgName + ")");
2592        }
2593
2594        return null;
2595    }
2596
2597    // called by JNI. PluginWidget function to launch a full-screen view using a
2598    // View object provided by the plugin class.
2599    private void showFullScreenPlugin(ViewManager.ChildView childView, int npp) {
2600        if (mWebView == null) {
2601            return;
2602        }
2603
2604        Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN);
2605        message.obj = childView.mView;
2606        message.arg1 = npp;
2607        message.sendToTarget();
2608    }
2609
2610    // called by JNI
2611    private void hideFullScreenPlugin() {
2612        if (mWebView == null) {
2613            return;
2614        }
2615        mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN)
2616                .sendToTarget();
2617    }
2618
2619    private ViewManager.ChildView createSurface(View pluginView) {
2620        if (mWebView == null) {
2621            return null;
2622        }
2623
2624        if (pluginView == null) {
2625            Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy");
2626            return null;
2627        }
2628
2629        // ensures the view system knows the view can redraw itself
2630        pluginView.setWillNotDraw(false);
2631
2632        if(pluginView instanceof SurfaceView)
2633            ((SurfaceView)pluginView).setZOrderOnTop(true);
2634
2635        ViewManager.ChildView view = mWebView.mViewManager.createView();
2636        view.mView = pluginView;
2637        return view;
2638    }
2639
2640    // called by JNI.  PluginWidget functions for creating an embedded View for
2641    // the surface drawing model.
2642    private ViewManager.ChildView addSurface(View pluginView, int x, int y,
2643                                             int width, int height) {
2644        ViewManager.ChildView view = createSurface(pluginView);
2645        view.attachView(x, y, width, height);
2646        return view;
2647    }
2648
2649    private void updateSurface(ViewManager.ChildView childView, int x, int y,
2650            int width, int height) {
2651        childView.attachView(x, y, width, height);
2652    }
2653
2654    private void destroySurface(ViewManager.ChildView childView) {
2655        childView.removeView();
2656    }
2657
2658    // called by JNI
2659    static class ShowRectData {
2660        int mLeft;
2661        int mTop;
2662        int mWidth;
2663        int mHeight;
2664        int mContentWidth;
2665        int mContentHeight;
2666        float mXPercentInDoc;
2667        float mXPercentInView;
2668        float mYPercentInDoc;
2669        float mYPercentInView;
2670    }
2671
2672    private void showRect(int left, int top, int width, int height,
2673            int contentWidth, int contentHeight, float xPercentInDoc,
2674            float xPercentInView, float yPercentInDoc, float yPercentInView) {
2675        if (mWebView != null) {
2676            ShowRectData data = new ShowRectData();
2677            data.mLeft = left;
2678            data.mTop = top;
2679            data.mWidth = width;
2680            data.mHeight = height;
2681            data.mContentWidth = contentWidth;
2682            data.mContentHeight = contentHeight;
2683            data.mXPercentInDoc = xPercentInDoc;
2684            data.mXPercentInView = xPercentInView;
2685            data.mYPercentInDoc = yPercentInDoc;
2686            data.mYPercentInView = yPercentInView;
2687            Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID,
2688                    data).sendToTarget();
2689        }
2690    }
2691
2692    // called by JNI
2693    private void centerFitRect(int x, int y, int width, int height) {
2694        if (mWebView == null) {
2695            return;
2696        }
2697        mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT,
2698                new Rect(x, y, x + width, y + height)).sendToTarget();
2699    }
2700
2701    // called by JNI
2702    private void setScrollbarModes(int hMode, int vMode) {
2703        if (mWebView == null) {
2704            return;
2705        }
2706        mWebView.mPrivateHandler.obtainMessage(WebView.SET_SCROLLBAR_MODES,
2707                hMode, vMode).sendToTarget();
2708    }
2709
2710    private void useMockDeviceOrientation() {
2711        mDeviceMotionAndOrientationManager.useMock();
2712    }
2713
2714    public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
2715            boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
2716        mDeviceMotionAndOrientationManager.setMockOrientation(canProvideAlpha, alpha,
2717                canProvideBeta, beta, canProvideGamma, gamma);
2718    }
2719
2720    protected DeviceMotionService getDeviceMotionService() {
2721        if (mDeviceMotionService == null) {
2722            mDeviceMotionService =
2723                    new DeviceMotionService(mDeviceMotionAndOrientationManager, mContext);
2724        }
2725        return mDeviceMotionService;
2726    }
2727
2728    protected DeviceOrientationService getDeviceOrientationService() {
2729        if (mDeviceOrientationService == null) {
2730            mDeviceOrientationService =
2731                    new DeviceOrientationService(mDeviceMotionAndOrientationManager, mContext);
2732        }
2733        return mDeviceOrientationService;
2734    }
2735
2736    private native void nativePause();
2737    private native void nativeResume();
2738    private native void nativeFreeMemory();
2739    private native void nativeFullScreenPluginHidden(int npp);
2740    private native boolean nativeValidNodeAndBounds(int frame, int node,
2741            Rect bounds);
2742
2743    private native ArrayList<Rect> nativeGetTouchHighlightRects(int x, int y,
2744            int slop);
2745
2746   private native void nativeAutoFillForm(int queryId);
2747}
2748