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