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