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