2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.webkit;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.graphics.Canvas;
22 import android.graphics.DrawFilter;
23 import android.graphics.Paint;
24 import android.graphics.PaintFlagsDrawFilter;
25 import android.graphics.Picture;
26 import android.graphics.Point;
27 import android.graphics.Rect;
28 import android.graphics.Region;
29 import android.os.Handler;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.Process;
33 import android.provider.Browser;
34 import android.util.Log;
35 import android.util.SparseBooleanArray;
36 import android.view.KeyEvent;
37 import android.view.SurfaceHolder;
38 import android.view.SurfaceView;
39 import android.view.View;
41 import java.util.ArrayList;
42 import java.util.Collection;
46 import junit.framework.Assert;
48 final class WebViewCore {
50 private static final String LOGTAG = "webcore";
53 // Load libwebcore during static initialization. This happens in the
54 // zygote process so it will be shared read-only across all app
56 System.loadLibrary("webcore");
60 * WebViewCore always executes in the same thread as the native webkit.
63 // The WebView that corresponds to this WebViewCore.
64 private WebView mWebView;
65 // Proxy for handling callbacks from native code
66 private final CallbackProxy mCallbackProxy;
67 // Settings object for maintaining all settings
68 private final WebSettings mSettings;
69 // Context for initializing the BrowserFrame with the proper assets.
70 private final Context mContext;
71 // The pointer to a native view object.
72 private int mNativeClass;
73 // The BrowserFrame is an interface to the native Frame component.
74 private BrowserFrame mBrowserFrame;
75 // Custom JS interfaces to add during the initialization.
76 private Map<String, Object> mJavascriptInterfaces;
78 * range is from 200 to 10,000. 0 is a special value means device-width. -1
81 private int mViewportWidth = -1;
84 * range is from 200 to 10,000. 0 is a special value means device-height. -1
87 private int mViewportHeight = -1;
90 * scale in percent, range is from 1 to 1000. 0 means undefined.
92 private int mViewportInitialScale = 0;
95 * scale in percent, range is from 1 to 1000. 0 means undefined.
97 private int mViewportMinimumScale = 0;
100 * scale in percent, range is from 1 to 1000. 0 means undefined.
102 private int mViewportMaximumScale = 0;
104 private boolean mViewportUserScalable = true;
107 * range is from 70 to 400.
108 * 0 is a special value means device-dpi. The default scale factor will be
110 * -1 means undefined. The default scale factor will be
111 * WebView.DEFAULT_SCALE_PERCENT.
113 private int mViewportDensityDpi = -1;
115 private int mRestoredScale = 0;
116 private int mRestoredScreenWidthScale = 0;
117 private int mRestoredX = 0;
118 private int mRestoredY = 0;
120 private int mWebkitScrollX = 0;
121 private int mWebkitScrollY = 0;
123 // If the site doesn't use viewport meta tag to specify the viewport, use
124 // DEFAULT_VIEWPORT_WIDTH as default viewport width
125 static final int DEFAULT_VIEWPORT_WIDTH = 800;
127 // The thread name used to identify the WebCore thread and for use in
128 // debugging other classes that require operation within the WebCore thread.
129 /* package */ static final String THREAD_NAME = "WebViewCoreThread";
131 public WebViewCore(Context context, WebView w, CallbackProxy proxy,
132 Map<String, Object> javascriptInterfaces) {
133 // No need to assign this in the WebCore thread.
134 mCallbackProxy = proxy;
136 mJavascriptInterfaces = javascriptInterfaces;
137 // This context object is used to initialize the WebViewCore during
138 // subwindow creation.
141 // We need to wait for the initial thread creation before sending
142 // a message to the WebCore thread.
143 // XXX: This is the only time the UI thread will wait for the WebCore
145 synchronized (WebViewCore.class) {
146 if (sWebCoreHandler == null) {
147 // Create a global thread and start it.
148 Thread t = new Thread(new WebCoreThread());
149 t.setName(THREAD_NAME);
152 WebViewCore.class.wait();
153 } catch (InterruptedException e) {
154 Log.e(LOGTAG, "Caught exception while waiting for thread " +
156 Log.e(LOGTAG, Log.getStackTraceString(e));
160 // Create an EventHub to handle messages before and after the thread is
162 mEventHub = new EventHub();
163 // Create a WebSettings object for maintaining all settings
164 mSettings = new WebSettings(mContext, mWebView);
165 // The WebIconDatabase needs to be initialized within the UI thread so
166 // just request the instance here.
167 WebIconDatabase.getInstance();
168 // Create the WebStorage singleton and the UI handler
169 WebStorage.getInstance().createUIHandler();
170 // Create the UI handler for GeolocationPermissions
171 GeolocationPermissions.getInstance().createUIHandler();
172 // Send a message to initialize the WebViewCore.
173 Message init = sWebCoreHandler.obtainMessage(
174 WebCoreThread.INITIALIZE, this);
175 sWebCoreHandler.sendMessage(init);
178 /* Initialize private data within the WebCore thread.
180 private void initialize() {
181 /* Initialize our private BrowserFrame class to handle all
182 * frame-related functions. We need to create a new view which
183 * in turn creates a C level FrameView and attaches it to the frame.
185 mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy,
186 mSettings, mJavascriptInterfaces);
187 mJavascriptInterfaces = null;
188 // Sync the native settings and also create the WebCore thread handler.
189 mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
190 // Create the handler and transfer messages for the IconDatabase
191 WebIconDatabase.getInstance().createHandler();
192 // Create the handler for WebStorage
193 WebStorage.getInstance().createHandler();
194 // Create the handler for GeolocationPermissions.
195 GeolocationPermissions.getInstance().createHandler();
196 // The transferMessages call will transfer all pending messages to the
197 // WebCore thread handler.
198 mEventHub.transferMessages();
200 // Send a message back to WebView to tell it that we have set up the
202 if (mWebView != null) {
203 Message.obtain(mWebView.mPrivateHandler,
204 WebView.WEBCORE_INITIALIZED_MSG_ID,
205 mNativeClass, 0).sendToTarget();
210 /* Handle the initialization of WebViewCore during subwindow creation. This
211 * method is called from the WebCore thread but it is called before the
212 * INITIALIZE message can be handled.
214 /* package */ void initializeSubwindow() {
215 // Go ahead and initialize the core components.
217 // Remove the INITIALIZE method so we don't try to initialize twice.
218 sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this);
221 /* Get the BrowserFrame component. This is used for subwindow creation and
222 * is called only from BrowserFrame in the WebCore thread. */
223 /* package */ BrowserFrame getBrowserFrame() {
224 return mBrowserFrame;
227 //-------------------------------------------------------------------------
229 //-------------------------------------------------------------------------
232 * Causes all timers to pause. This applies to all WebViews in the current
235 public static void pauseTimers() {
236 if (BrowserFrame.sJavaBridge == null) {
237 throw new IllegalStateException(
238 "No WebView has been created in this process!");
240 BrowserFrame.sJavaBridge.pause();
244 * Resume all timers. This applies to all WebViews in the current process.
246 public static void resumeTimers() {
247 if (BrowserFrame.sJavaBridge == null) {
248 throw new IllegalStateException(
249 "No WebView has been created in this process!");
251 BrowserFrame.sJavaBridge.resume();
254 public WebSettings getSettings() {
259 * Add an error message to the client's console.
260 * @param message The message to add
261 * @param lineNumber the line on which the error occurred
262 * @param sourceID the filename of the source that caused the error.
264 protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
265 mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
269 * Invoke a javascript alert.
270 * @param message The message displayed in the alert.
272 protected void jsAlert(String url, String message) {
273 mCallbackProxy.onJsAlert(url, message);
277 * Notify the browser that the origin has exceeded it's database quota.
278 * @param url The URL that caused the overflow.
279 * @param databaseIdentifier The identifier of the database.
280 * @param currentQuota The current quota for the origin.
281 * @param estimatedSize The estimated size of the database.
283 protected void exceededDatabaseQuota(String url,
284 String databaseIdentifier,
286 long estimatedSize) {
287 // Inform the callback proxy of the quota overflow. Send an object
288 // that encapsulates a call to the nativeSetDatabaseQuota method to
289 // awaken the sleeping webcore thread when a decision from the
290 // client to allow or deny quota is available.
291 mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
292 currentQuota, estimatedSize, getUsedQuota(),
293 new WebStorage.QuotaUpdater() {
294 public void updateQuota(long quota) {
295 nativeSetNewStorageLimit(quota);
301 * Notify the browser that the appcache has exceeded its max size.
302 * @param spaceNeeded is the amount of disk space that would be needed
303 * in order for the last appcache operation to succeed.
305 protected void reachedMaxAppCacheSize(long spaceNeeded) {
306 mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(),
307 new WebStorage.QuotaUpdater() {
308 public void updateQuota(long quota) {
309 nativeSetNewStorageLimit(quota);
314 protected void populateVisitedLinks() {
315 ValueCallback callback = new ValueCallback<String[]>() {
316 public void onReceiveValue(String[] value) {
317 sendMessage(EventHub.POPULATE_VISITED_LINKS, (Object)value);
320 mCallbackProxy.getVisitedHistory(callback);
324 * Shows a prompt to ask the user to set the Geolocation permission state
325 * for the given origin.
326 * @param origin The origin for which Geolocation permissions are
329 protected void geolocationPermissionsShowPrompt(String origin) {
330 mCallbackProxy.onGeolocationPermissionsShowPrompt(origin,
331 new GeolocationPermissions.Callback() {
332 public void invoke(String origin, boolean allow, boolean remember) {
333 GeolocationPermissionsData data = new GeolocationPermissionsData();
334 data.mOrigin = origin;
336 data.mRemember = remember;
337 // Marshall to WebCore thread.
338 sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data);
344 * Hides the Geolocation permissions prompt.
346 protected void geolocationPermissionsHidePrompt() {
347 mCallbackProxy.onGeolocationPermissionsHidePrompt();
351 * Invoke a javascript confirm dialog.
352 * @param message The message displayed in the dialog.
353 * @return True if the user confirmed or false if the user cancelled.
355 protected boolean jsConfirm(String url, String message) {
356 return mCallbackProxy.onJsConfirm(url, message);
360 * Invoke a javascript prompt dialog.
361 * @param message The message to be displayed in the dialog.
362 * @param defaultValue The default value in the prompt input.
363 * @return The input from the user or null to indicate the user cancelled
366 protected String jsPrompt(String url, String message, String defaultValue) {
367 return mCallbackProxy.onJsPrompt(url, message, defaultValue);
371 * Invoke a javascript before unload dialog.
372 * @param url The url that is requesting the dialog.
373 * @param message The message displayed in the dialog.
374 * @return True if the user confirmed or false if the user cancelled. False
375 * will cancel the navigation.
377 protected boolean jsUnload(String url, String message) {
378 return mCallbackProxy.onJsBeforeUnload(url, message);
383 * Callback to notify that a JavaScript execution timeout has occured.
384 * @return True if the JavaScript execution should be interrupted. False
385 * will continue the execution.
387 protected boolean jsInterrupt() {
388 return mCallbackProxy.onJsTimeout();
391 //-------------------------------------------------------------------------
393 //-------------------------------------------------------------------------
395 static native String nativeFindAddress(String addr, boolean caseInsensitive);
398 * Empty the picture set.
400 private native void nativeClearContent();
403 * Create a flat picture from the set of pictures.
405 private native void nativeCopyContentToPicture(Picture picture);
408 * Draw the picture set with a background color. Returns true
409 * if some individual picture took too long to draw and can be
410 * split into parts. Called from the UI thread.
412 private native boolean nativeDrawContent(Canvas canvas, int color);
415 * check to see if picture is blank and in progress
417 private native boolean nativePictureReady();
420 * Redraw a portion of the picture set. The Point wh returns the
421 * width and height of the overall picture.
423 private native boolean nativeRecordContent(Region invalRegion, Point wh);
426 * Splits slow parts of the picture set. Called from the webkit
427 * thread after nativeDrawContent returns true.
429 private native void nativeSplitContent();
431 private native boolean nativeKey(int keyCode, int unichar,
432 int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
435 private native void nativeClick(int framePtr, int nodePtr);
437 private native void nativeSendListBoxChoices(boolean[] choices, int size);
439 private native void nativeSendListBoxChoice(int choice);
441 /* Tell webkit what its width and height are, for the purposes
442 of layout/line-breaking. These coordinates are in document space,
443 which is the same as View coords unless we have zoomed the document
445 screenWidth is used by layout to wrap column around. If viewport uses
446 fixed size, screenWidth can be different from width with zooming.
447 should this be called nativeSetViewPortSize?
449 private native void nativeSetSize(int width, int height, int screenWidth,
450 float scale, int realScreenWidth, int screenHeight,
451 boolean ignoreHeight);
453 private native int nativeGetContentMinPrefWidth();
455 // Start: functions that deal with text editing
456 private native void nativeReplaceTextfieldText(
457 int oldStart, int oldEnd, String replace, int newStart, int newEnd,
460 private native void passToJs(int gen,
461 String currentText, int keyCode, int keyValue, boolean down,
462 boolean cap, boolean fn, boolean sym);
464 private native void nativeSetFocusControllerActive(boolean active);
466 private native void nativeSaveDocumentState(int frame);
468 private native void nativeMoveMouse(int framePtr, int x, int y);
470 private native void nativeMoveMouseIfLatest(int moveGeneration,
471 int framePtr, int x, int y);
473 private native String nativeRetrieveHref(int framePtr, int nodePtr);
475 private native void nativeTouchUp(int touchGeneration,
476 int framePtr, int nodePtr, int x, int y);
478 private native boolean nativeHandleTouchEvent(int action, int x, int y);
480 private native void nativeUpdateFrameCache();
482 private native void nativeSetBackgroundColor(int color);
484 private native void nativeDumpDomTree(boolean useFile);
486 private native void nativeDumpRenderTree(boolean useFile);
488 private native void nativeDumpNavTree();
490 private native void nativeSetJsFlags(String flags);
493 * Delete text from start to end in the focused textfield. If there is no
494 * focus, or if start == end, silently fail. If start and end are out of
496 * @param start Beginning of selection to delete.
497 * @param end End of selection to delete.
498 * @param textGeneration Text generation number when delete was pressed.
500 private native void nativeDeleteSelection(int start, int end,
504 * Set the selection to (start, end) in the focused textfield. If start and
505 * end are out of order, swap them.
506 * @param start Beginning of selection.
507 * @param end End of selection.
509 private native void nativeSetSelection(int start, int end);
511 private native String nativeGetSelection(Region sel);
513 // Register a scheme to be treated as local scheme so that it can access
514 // local asset files for resources
515 private native void nativeRegisterURLSchemeAsLocal(String scheme);
518 * Inform webcore that the user has decided whether to allow or deny new
519 * quota for the current origin or more space for the app cache, and that
520 * the main thread should wake up now.
521 * @param limit Is the new quota for an origin or new app cache max size.
523 private native void nativeSetNewStorageLimit(long limit);
525 private native void nativeUpdatePluginState(int framePtr, int nodePtr, int state);
528 * Provide WebCore with a Geolocation permission state for the specified
530 * @param origin The origin for which Geolocation permissions are provided.
531 * @param allow Whether Geolocation permissions are allowed.
532 * @param remember Whether this decision should be remembered beyond the
533 * life of the current page.
535 private native void nativeGeolocationPermissionsProvide(String origin, boolean allow, boolean remember);
538 * Provide WebCore with the previously visted links from the history database
540 private native void nativeProvideVisitedHistory(String[] history);
542 // EventHub for processing messages
543 private final EventHub mEventHub;
544 // WebCore thread handler
545 private static Handler sWebCoreHandler;
546 // Class for providing Handler creation inside the WebCore thread.
547 private static class WebCoreThread implements Runnable {
548 // Message id for initializing a new WebViewCore.
549 private static final int INITIALIZE = 0;
550 private static final int REDUCE_PRIORITY = 1;
551 private static final int RESUME_PRIORITY = 2;
552 private static final int CACHE_TICKER = 3;
553 private static final int BLOCK_CACHE_TICKER = 4;
554 private static final int RESUME_CACHE_TICKER = 5;
556 private static final int CACHE_TICKER_INTERVAL = 60 * 1000; // 1 minute
558 private static boolean mCacheTickersBlocked = true;
562 Assert.assertNull(sWebCoreHandler);
563 synchronized (WebViewCore.class) {
564 sWebCoreHandler = new Handler() {
566 public void handleMessage(Message msg) {
569 WebViewCore core = (WebViewCore) msg.obj;
573 case REDUCE_PRIORITY:
574 // 3 is an adjustable number.
575 Process.setThreadPriority(
576 Process.THREAD_PRIORITY_DEFAULT + 3 *
577 Process.THREAD_PRIORITY_LESS_FAVORABLE);
580 case RESUME_PRIORITY:
581 Process.setThreadPriority(
582 Process.THREAD_PRIORITY_DEFAULT);
586 if (!mCacheTickersBlocked) {
587 CacheManager.endCacheTransaction();
588 CacheManager.startCacheTransaction();
590 obtainMessage(CACHE_TICKER),
591 CACHE_TICKER_INTERVAL);
595 case BLOCK_CACHE_TICKER:
596 if (CacheManager.endCacheTransaction()) {
597 mCacheTickersBlocked = true;
601 case RESUME_CACHE_TICKER:
602 if (CacheManager.startCacheTransaction()) {
603 mCacheTickersBlocked = false;
609 WebViewCore.class.notify();
615 static class BaseUrlData {
623 static class CursorData {
625 CursorData(int frame, int node, int x, int y) {
636 static class JSInterfaceData {
638 String mInterfaceName;
641 static class JSKeyData {
646 static class PostUrlData {
651 static class ReplaceTextData {
658 static class TextSelectionData {
659 public TextSelectionData(int start, int end) {
667 static class TouchUpData {
675 static class TouchEventData {
676 int mAction; // MotionEvent.getAction()
681 static class PluginStateData {
687 static class GeolocationPermissionsData {
693 static final String[] HandlerDebugString = {
694 "UPDATE_FRAME_CACHE_IF_LOADING", // = 98
695 "SCROLL_TEXT_INPUT", // = 99
696 "LOAD_URL", // = 100;
697 "STOP_LOADING", // = 101;
699 "KEY_DOWN", // = 103;
701 "VIEW_SIZE_CHANGED", // = 105;
702 "GO_BACK_FORWARD", // = 106;
703 "SET_SCROLL_OFFSET", // = 107;
704 "RESTORE_STATE", // = 108;
705 "PAUSE_TIMERS", // = 109;
706 "RESUME_TIMERS", // = 110;
707 "CLEAR_CACHE", // = 111;
708 "CLEAR_HISTORY", // = 112;
709 "SET_SELECTION", // = 113;
710 "REPLACE_TEXT", // = 114;
711 "PASS_TO_JS", // = 115;
712 "SET_GLOBAL_BOUNDS", // = 116;
713 "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117;
715 "SET_NETWORK_STATE", // = 119;
716 "DOC_HAS_IMAGES", // = 120;
718 "DELETE_SELECTION", // = 122;
719 "LISTBOX_CHOICES", // = 123;
720 "SINGLE_LISTBOX_CHOICE", // = 124;
721 "MESSAGE_RELAY", // = 125;
722 "SET_BACKGROUND_COLOR", // = 126;
723 "PLUGIN_STATE", // = 127;
724 "SAVE_DOCUMENT_STATE", // = 128;
725 "GET_SELECTION", // = 129;
726 "WEBKIT_DRAW", // = 130;
727 "SYNC_SCROLL", // = 131;
728 "POST_URL", // = 132;
729 "SPLIT_PICTURE_SET", // = 133;
730 "CLEAR_CONTENT", // = 134;
731 "SET_MOVE_MOUSE", // = 135;
732 "SET_MOVE_MOUSE_IF_LATEST", // = 136;
733 "REQUEST_CURSOR_HREF", // = 137;
734 "ADD_JS_INTERFACE", // = 138;
735 "LOAD_DATA", // = 139;
736 "TOUCH_UP", // = 140;
737 "TOUCH_EVENT", // = 141;
738 "SET_ACTIVE", // = 142;
740 "ON_RESUME", // = 144
741 "FREE_MEMORY", // = 145
746 static final int UPDATE_FRAME_CACHE_IF_LOADING = 98;
747 static final int SCROLL_TEXT_INPUT = 99;
748 static final int LOAD_URL = 100;
749 static final int STOP_LOADING = 101;
750 static final int RELOAD = 102;
751 static final int KEY_DOWN = 103;
752 static final int KEY_UP = 104;
753 static final int VIEW_SIZE_CHANGED = 105;
754 static final int GO_BACK_FORWARD = 106;
755 static final int SET_SCROLL_OFFSET = 107;
756 static final int RESTORE_STATE = 108;
757 static final int PAUSE_TIMERS = 109;
758 static final int RESUME_TIMERS = 110;
759 static final int CLEAR_CACHE = 111;
760 static final int CLEAR_HISTORY = 112;
761 static final int SET_SELECTION = 113;
762 static final int REPLACE_TEXT = 114;
763 static final int PASS_TO_JS = 115;
764 static final int SET_GLOBAL_BOUNDS = 116;
765 static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117;
766 static final int CLICK = 118;
767 static final int SET_NETWORK_STATE = 119;
768 static final int DOC_HAS_IMAGES = 120;
769 static final int DELETE_SELECTION = 122;
770 static final int LISTBOX_CHOICES = 123;
771 static final int SINGLE_LISTBOX_CHOICE = 124;
772 static final int MESSAGE_RELAY = 125;
773 static final int SET_BACKGROUND_COLOR = 126;
774 static final int PLUGIN_STATE = 127; // plugin notifications
775 static final int SAVE_DOCUMENT_STATE = 128;
776 static final int GET_SELECTION = 129;
777 static final int WEBKIT_DRAW = 130;
778 static final int SYNC_SCROLL = 131;
779 static final int POST_URL = 132;
780 static final int SPLIT_PICTURE_SET = 133;
781 static final int CLEAR_CONTENT = 134;
784 static final int SET_MOVE_MOUSE = 135;
785 static final int SET_MOVE_MOUSE_IF_LATEST = 136;
786 static final int REQUEST_CURSOR_HREF = 137;
787 static final int ADD_JS_INTERFACE = 138;
788 static final int LOAD_DATA = 139;
791 static final int TOUCH_UP = 140;
792 // message used to pass UI touch events to WebCore
793 static final int TOUCH_EVENT = 141;
795 // Used to tell the focus controller not to draw the blinking cursor,
796 // based on whether the WebView has focus and whether the WebView's
797 // cursor matches the webpage's focus.
798 static final int SET_ACTIVE = 142;
800 // lifecycle activities for just this DOM (unlike pauseTimers, which
802 static final int ON_PAUSE = 143;
803 static final int ON_RESUME = 144;
804 static final int FREE_MEMORY = 145;
806 // Network-based messaging
807 static final int CLEAR_SSL_PREF_TABLE = 150;
809 // Test harness messages
810 static final int REQUEST_EXT_REPRESENTATION = 160;
811 static final int REQUEST_DOC_AS_TEXT = 161;
814 static final int DUMP_DOMTREE = 170;
815 static final int DUMP_RENDERTREE = 171;
816 static final int DUMP_NAVTREE = 172;
818 static final int SET_JS_FLAGS = 173;
820 static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180;
822 static final int POPULATE_VISITED_LINKS = 181;
824 // private message ids
825 private static final int DESTROY = 200;
827 // Private handler for WebCore messages.
828 private Handler mHandler;
829 // Message queue for containing messages before the WebCore thread is
831 private ArrayList<Message> mMessages = new ArrayList<Message>();
832 // Flag for blocking messages. This is used during DESTROY to avoid
833 // posting more messages to the EventHub or to WebView's event handler.
834 private boolean mBlockMessages;
837 private int mSavedPriority;
840 * Prevent other classes from creating an EventHub.
842 private EventHub() {}
845 * Transfer all messages to the newly created webcore thread handler.
847 private void transferMessages() {
848 mTid = Process.myTid();
849 mSavedPriority = Process.getThreadPriority(mTid);
851 mHandler = new Handler() {
853 public void handleMessage(Message msg) {
854 if (DebugFlags.WEB_VIEW_CORE) {
855 Log.v(LOGTAG, (msg.what < UPDATE_FRAME_CACHE_IF_LOADING
857 > FREE_MEMORY ? Integer.toString(msg.what)
858 : HandlerDebugString[msg.what
859 - UPDATE_FRAME_CACHE_IF_LOADING])
860 + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
861 + " obj=" + msg.obj);
869 // Time to take down the world. Cancel all pending
870 // loads and destroy the native view and frame.
871 synchronized (WebViewCore.this) {
872 mBrowserFrame.destroy();
873 mBrowserFrame = null;
874 mSettings.onDestroyed();
880 case UPDATE_FRAME_CACHE_IF_LOADING:
881 nativeUpdateFrameCacheIfLoading();
884 case SCROLL_TEXT_INPUT:
885 nativeScrollFocusedTextInput(
886 ((Float) msg.obj).floatValue(), msg.arg1);
890 loadUrl((String) msg.obj);
894 PostUrlData param = (PostUrlData) msg.obj;
895 mBrowserFrame.postUrl(param.mUrl, param.mPostData);
899 BaseUrlData loadParams = (BaseUrlData) msg.obj;
900 String baseUrl = loadParams.mBaseUrl;
901 if (baseUrl != null) {
902 int i = baseUrl.indexOf(':');
906 * WebView#loadDataWithBaseURL} can access
907 * local asset files as long as the data is
908 * valid. In the new WebKit, the restriction
909 * is tightened. To be compatible with 1.0,
910 * we automatically add the scheme of the
911 * baseUrl for local access as long as it is
912 * not http(s)/ftp(s)/about/javascript
914 String scheme = baseUrl.substring(0, i);
915 if (!scheme.startsWith("http") &&
916 !scheme.startsWith("ftp") &&
917 !scheme.startsWith("about") &&
918 !scheme.startsWith("javascript")) {
919 nativeRegisterURLSchemeAsLocal(scheme);
923 mBrowserFrame.loadData(baseUrl,
925 loadParams.mMimeType,
926 loadParams.mEncoding,
927 loadParams.mFailUrl);
931 // If the WebCore has committed the load, but not
932 // finished the first layout yet, we need to set
933 // first layout done to trigger the interpreted side sync
934 // up with native side
935 if (mBrowserFrame.committed()
936 && !mBrowserFrame.firstLayoutDone()) {
937 mBrowserFrame.didFirstLayout();
939 // Do this after syncing up the layout state.
944 mBrowserFrame.reload(false);
948 key((KeyEvent) msg.obj, true);
952 key((KeyEvent) msg.obj, false);
956 nativeClick(msg.arg1, msg.arg2);
959 case VIEW_SIZE_CHANGED: {
960 WebView.ViewSizeData data =
961 (WebView.ViewSizeData) msg.obj;
962 viewSizeChanged(data.mWidth, data.mHeight,
963 data.mTextWrapWidth, data.mScale,
967 case SET_SCROLL_OFFSET:
968 // note: these are in document coordinates
970 Point pt = (Point) msg.obj;
971 nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
974 case SET_GLOBAL_BOUNDS:
975 Rect r = (Rect) msg.obj;
976 nativeSetGlobalBounds(r.left, r.top, r.width(),
980 case GO_BACK_FORWARD:
981 // If it is a standard load and the load is not
982 // committed yet, we interpret BACK as RELOAD
983 if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
984 (mBrowserFrame.loadType() ==
985 BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
986 mBrowserFrame.reload(true);
988 mBrowserFrame.goBackOrForward(msg.arg1);
994 restoreState(msg.arg1);
998 mSavedPriority = Process.getThreadPriority(mTid);
999 Process.setThreadPriority(mTid,
1000 Process.THREAD_PRIORITY_BACKGROUND);
1002 if (CacheManager.disableTransaction()) {
1003 WebCoreThread.mCacheTickersBlocked = true;
1004 sWebCoreHandler.removeMessages(
1005 WebCoreThread.CACHE_TICKER);
1010 Process.setThreadPriority(mTid, mSavedPriority);
1012 if (CacheManager.enableTransaction()) {
1013 WebCoreThread.mCacheTickersBlocked = false;
1014 sWebCoreHandler.sendMessageDelayed(
1015 sWebCoreHandler.obtainMessage(
1016 WebCoreThread.CACHE_TICKER),
1017 WebCoreThread.CACHE_TICKER_INTERVAL);
1035 PluginStateData psd = (PluginStateData) msg.obj;
1036 nativeUpdatePluginState(psd.mFrame, psd.mNode, psd.mState);
1039 case SET_NETWORK_STATE:
1040 if (BrowserFrame.sJavaBridge == null) {
1041 throw new IllegalStateException("No WebView " +
1042 "has been created in this process!");
1044 BrowserFrame.sJavaBridge
1045 .setNetworkOnLine(msg.arg1 == 1);
1049 clearCache(msg.arg1 == 1);
1053 mCallbackProxy.getBackForwardList().
1054 close(mBrowserFrame.mNativeFrame);
1058 ReplaceTextData rep = (ReplaceTextData) msg.obj;
1059 nativeReplaceTextfieldText(msg.arg1, msg.arg2,
1060 rep.mReplace, rep.mNewStart, rep.mNewEnd,
1061 rep.mTextGeneration);
1065 JSKeyData jsData = (JSKeyData) msg.obj;
1066 KeyEvent evt = jsData.mEvent;
1067 int keyCode = evt.getKeyCode();
1068 int keyValue = evt.getUnicodeChar();
1069 int generation = msg.arg1;
1070 passToJs(generation,
1071 jsData.mCurrentText,
1075 evt.isShiftPressed(), evt.isAltPressed(),
1076 evt.isSymPressed());
1080 case SAVE_DOCUMENT_STATE: {
1081 CursorData cDat = (CursorData) msg.obj;
1082 nativeSaveDocumentState(cDat.mFrame);
1086 case CLEAR_SSL_PREF_TABLE:
1087 Network.getInstance(mContext)
1088 .clearUserSslPrefTable();
1092 TouchUpData touchUpData = (TouchUpData) msg.obj;
1093 nativeTouchUp(touchUpData.mMoveGeneration,
1094 touchUpData.mFrame, touchUpData.mNode,
1095 touchUpData.mX, touchUpData.mY);
1099 TouchEventData ted = (TouchEventData) msg.obj;
1101 mWebView.mPrivateHandler,
1102 WebView.PREVENT_TOUCH_ID, ted.mAction,
1103 nativeHandleTouchEvent(ted.mAction, ted.mX,
1104 ted.mY) ? 1 : 0).sendToTarget();
1109 nativeSetFocusControllerActive(msg.arg1 == 1);
1112 case ADD_JS_INTERFACE:
1113 JSInterfaceData jsData = (JSInterfaceData) msg.obj;
1114 mBrowserFrame.addJavascriptInterface(jsData.mObject,
1115 jsData.mInterfaceName);
1118 case REQUEST_EXT_REPRESENTATION:
1119 mBrowserFrame.externalRepresentation(
1123 case REQUEST_DOC_AS_TEXT:
1124 mBrowserFrame.documentAsText((Message) msg.obj);
1127 case SET_MOVE_MOUSE:
1128 CursorData cursorData = (CursorData) msg.obj;
1129 nativeMoveMouse(cursorData.mFrame,
1130 cursorData.mX, cursorData.mY);
1133 case SET_MOVE_MOUSE_IF_LATEST:
1134 CursorData cData = (CursorData) msg.obj;
1135 nativeMoveMouseIfLatest(cData.mMoveGeneration,
1137 cData.mX, cData.mY);
1140 case REQUEST_CURSOR_HREF: {
1141 Message hrefMsg = (Message) msg.obj;
1142 String res = nativeRetrieveHref(msg.arg1, msg.arg2);
1143 hrefMsg.getData().putString("url", res);
1144 hrefMsg.sendToTarget();
1148 case UPDATE_CACHE_AND_TEXT_ENTRY:
1149 nativeUpdateFrameCache();
1150 // FIXME: this should provide a minimal rectangle
1151 if (mWebView != null) {
1152 mWebView.postInvalidate();
1154 sendUpdateTextEntry();
1157 case DOC_HAS_IMAGES:
1158 Message imageResult = (Message) msg.obj;
1160 mBrowserFrame.documentHasImages() ? 1 : 0;
1161 imageResult.sendToTarget();
1164 case DELETE_SELECTION:
1165 TextSelectionData deleteSelectionData
1166 = (TextSelectionData) msg.obj;
1167 nativeDeleteSelection(deleteSelectionData.mStart,
1168 deleteSelectionData.mEnd, msg.arg1);
1172 nativeSetSelection(msg.arg1, msg.arg2);
1175 case LISTBOX_CHOICES:
1176 SparseBooleanArray choices = (SparseBooleanArray)
1178 int choicesSize = msg.arg1;
1179 boolean[] choicesArray = new boolean[choicesSize];
1180 for (int c = 0; c < choicesSize; c++) {
1181 choicesArray[c] = choices.get(c);
1183 nativeSendListBoxChoices(choicesArray,
1187 case SINGLE_LISTBOX_CHOICE:
1188 nativeSendListBoxChoice(msg.arg1);
1191 case SET_BACKGROUND_COLOR:
1192 nativeSetBackgroundColor(msg.arg1);
1196 String str = nativeGetSelection((Region) msg.obj);
1197 Message.obtain(mWebView.mPrivateHandler
1198 , WebView.UPDATE_CLIPBOARD, str)
1203 nativeDumpDomTree(msg.arg1 == 1);
1206 case DUMP_RENDERTREE:
1207 nativeDumpRenderTree(msg.arg1 == 1);
1211 nativeDumpNavTree();
1215 nativeSetJsFlags((String)msg.obj);
1218 case GEOLOCATION_PERMISSIONS_PROVIDE:
1219 GeolocationPermissionsData data =
1220 (GeolocationPermissionsData) msg.obj;
1221 nativeGeolocationPermissionsProvide(data.mOrigin,
1222 data.mAllow, data.mRemember);
1226 mWebkitScrollX = msg.arg1;
1227 mWebkitScrollY = msg.arg2;
1230 case SPLIT_PICTURE_SET:
1231 nativeSplitContent();
1232 mSplitPictureIsScheduled = false;
1236 // Clear the view so that onDraw() will draw nothing
1237 // but white background
1238 // (See public method WebView.clearView)
1239 nativeClearContent();
1243 if (msg.obj instanceof Message) {
1244 ((Message) msg.obj).sendToTarget();
1248 case POPULATE_VISITED_LINKS:
1249 nativeProvideVisitedHistory((String[])msg.obj);
1254 // Take all queued messages and resend them to the new handler.
1255 synchronized (this) {
1256 int size = mMessages.size();
1257 for (int i = 0; i < size; i++) {
1258 mHandler.sendMessage(mMessages.get(i));
1265 * Send a message internally to the queue or to the handler
1267 private synchronized void sendMessage(Message msg) {
1268 if (mBlockMessages) {
1271 if (mMessages != null) {
1274 mHandler.sendMessage(msg);
1278 private synchronized void removeMessages(int what) {
1279 if (mBlockMessages) {
1282 if (what == EventHub.WEBKIT_DRAW) {
1283 mDrawIsScheduled = false;
1285 if (mMessages != null) {
1286 Log.w(LOGTAG, "Not supported in this case.");
1288 mHandler.removeMessages(what);
1292 private synchronized boolean hasMessages(int what) {
1293 if (mBlockMessages) {
1296 if (mMessages != null) {
1297 Log.w(LOGTAG, "hasMessages() is not supported in this case.");
1300 return mHandler.hasMessages(what);
1304 private synchronized void sendMessageDelayed(Message msg, long delay) {
1305 if (mBlockMessages) {
1308 mHandler.sendMessageDelayed(msg, delay);
1312 * Send a message internally to the front of the queue.
1314 private synchronized void sendMessageAtFrontOfQueue(Message msg) {
1315 if (mBlockMessages) {
1318 if (mMessages != null) {
1319 mMessages.add(0, msg);
1321 mHandler.sendMessageAtFrontOfQueue(msg);
1326 * Remove all the messages.
1328 private synchronized void removeMessages() {
1329 // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed
1330 mDrawIsScheduled = false;
1331 mSplitPictureIsScheduled = false;
1332 if (mMessages != null) {
1335 mHandler.removeCallbacksAndMessages(null);
1340 * Block sending messages to the EventHub.
1342 private synchronized void blockMessages() {
1343 mBlockMessages = true;
1347 //-------------------------------------------------------------------------
1348 // Methods called by host activity (in the same thread)
1349 //-------------------------------------------------------------------------
1351 void stopLoading() {
1352 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
1353 if (mBrowserFrame != null) {
1354 mBrowserFrame.stopLoading();
1358 //-------------------------------------------------------------------------
1359 // Methods called by WebView
1360 // If it refers to local variable, it needs synchronized().
1361 // If it needs WebCore, it has to send message.
1362 //-------------------------------------------------------------------------
1364 void sendMessage(Message msg) {
1365 mEventHub.sendMessage(msg);
1368 void sendMessage(int what) {
1369 mEventHub.sendMessage(Message.obtain(null, what));
1372 void sendMessage(int what, Object obj) {
1373 mEventHub.sendMessage(Message.obtain(null, what, obj));
1376 void sendMessage(int what, int arg1) {
1377 // just ignore the second argument (make it 0)
1378 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0));
1381 void sendMessage(int what, int arg1, int arg2) {
1382 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2));
1385 void sendMessage(int what, int arg1, Object obj) {
1386 // just ignore the second argument (make it 0)
1387 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj));
1390 void sendMessage(int what, int arg1, int arg2, Object obj) {
1391 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj));
1394 void sendMessageDelayed(int what, Object obj, long delay) {
1395 mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay);
1398 void removeMessages(int what) {
1399 mEventHub.removeMessages(what);
1402 void removeMessages() {
1403 mEventHub.removeMessages();
1407 * Removes pending messages and trigger a DESTROY message to send to
1409 * Called from UI thread.
1412 // We don't want anyone to post a message between removing pending
1413 // messages and sending the destroy message.
1414 synchronized (mEventHub) {
1415 // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
1416 // be preserved even the WebView is destroyed.
1417 // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
1418 boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
1419 boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
1420 mEventHub.removeMessages();
1421 mEventHub.sendMessageAtFrontOfQueue(
1422 Message.obtain(null, EventHub.DESTROY));
1424 mEventHub.sendMessageAtFrontOfQueue(
1425 Message.obtain(null, EventHub.PAUSE_TIMERS));
1428 mEventHub.sendMessageAtFrontOfQueue(
1429 Message.obtain(null, EventHub.RESUME_TIMERS));
1431 mEventHub.blockMessages();
1435 //-------------------------------------------------------------------------
1436 // WebViewCore private methods
1437 //-------------------------------------------------------------------------
1439 private void clearCache(boolean includeDiskFiles) {
1440 mBrowserFrame.clearCache();
1441 if (includeDiskFiles) {
1442 CacheManager.removeAllCacheFiles();
1446 private void loadUrl(String url) {
1447 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
1448 mBrowserFrame.loadUrl(url);
1451 private void key(KeyEvent evt, boolean isDown) {
1452 if (DebugFlags.WEB_VIEW_CORE) {
1453 Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
1456 int keyCode = evt.getKeyCode();
1457 if (!nativeKey(keyCode, evt.getUnicodeChar(),
1458 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
1460 isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
1461 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
1462 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
1463 if (DebugFlags.WEB_VIEW_CORE) {
1464 Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
1466 if (mWebView != null && evt.isDown()) {
1467 Message.obtain(mWebView.mPrivateHandler,
1468 WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
1472 // bubble up the event handling
1473 // but do not bubble up the ENTER key, which would open the search
1474 // bar without any text.
1475 mCallbackProxy.onUnhandledKeyEvent(evt);
1479 // These values are used to avoid requesting a layout based on old values
1480 private int mCurrentViewWidth = 0;
1481 private int mCurrentViewHeight = 0;
1482 private float mCurrentViewScale = 1.0f;
1484 // notify webkit that our virtual view size changed size (after inv-zoom)
1485 private void viewSizeChanged(int w, int h, int textwrapWidth, float scale,
1486 boolean ignoreHeight) {
1487 if (DebugFlags.WEB_VIEW_CORE) {
1488 Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
1489 + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
1492 Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
1496 if (mSettings.getUseWideViewPort()) {
1497 if (mViewportWidth == -1) {
1498 if (mSettings.getLayoutAlgorithm() ==
1499 WebSettings.LayoutAlgorithm.NORMAL) {
1500 width = DEFAULT_VIEWPORT_WIDTH;
1503 * if a page's minimum preferred width is wider than the
1504 * given "w", use it instead to get better layout result. If
1505 * we start a page with MAX_ZOOM_WIDTH, "w" will be always
1506 * wider. If we start a page with screen width, due to the
1507 * delay between {@link #didFirstLayout} and
1508 * {@link #viewSizeChanged},
1509 * {@link #nativeGetContentMinPrefWidth} will return a more
1510 * accurate value than initial 0 to result a better layout.
1511 * In the worse case, the native width will be adjusted when
1512 * next zoom or screen orientation change happens.
1514 width = Math.max(w, Math.max(DEFAULT_VIEWPORT_WIDTH,
1515 nativeGetContentMinPrefWidth()));
1518 width = Math.max(w, mViewportWidth);
1521 nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
1522 textwrapWidth, scale, w, h, ignoreHeight);
1523 // Remember the current width and height
1524 boolean needInvalidate = (mCurrentViewWidth == 0);
1525 mCurrentViewWidth = w;
1526 mCurrentViewHeight = h;
1527 mCurrentViewScale = scale;
1528 if (needInvalidate) {
1529 // ensure {@link #webkitDraw} is called as we were blocking in
1530 // {@link #contentDraw} when mCurrentViewWidth is 0
1531 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
1534 mEventHub.sendMessage(Message.obtain(null,
1535 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY));
1538 private void sendUpdateTextEntry() {
1539 if (mWebView != null) {
1540 Message.obtain(mWebView.mPrivateHandler,
1541 WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget();
1545 // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
1546 // callbacks. Computes the sum of database quota for all origins.
1547 private long getUsedQuota() {
1548 WebStorage webStorage = WebStorage.getInstance();
1549 Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
1551 if (origins == null) {
1555 for (WebStorage.Origin website : origins) {
1556 usedQuota += website.getQuota();
1561 // Used to avoid posting more than one draw message.
1562 private boolean mDrawIsScheduled;
1564 // Used to avoid posting more than one split picture message.
1565 private boolean mSplitPictureIsScheduled;
1567 // Used to suspend drawing.
1568 private boolean mDrawIsPaused;
1570 // mRestoreState is set in didFirstLayout(), and reset in the next
1571 // webkitDraw after passing it to the UI thread.
1572 private RestoreState mRestoreState = null;
1574 static class RestoreState {
1578 float mTextWrapScale;
1579 float mDefaultScale;
1582 boolean mMobileSite;
1585 static class DrawData {
1587 mInvalRegion = new Region();
1588 mWidthHeight = new Point();
1590 Region mInvalRegion;
1594 RestoreState mRestoreState; // only non-null if it is for the first
1595 // picture set after the first layout
1598 private void webkitDraw() {
1599 mDrawIsScheduled = false;
1600 DrawData draw = new DrawData();
1601 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
1602 if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
1604 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
1607 if (mWebView != null) {
1608 // Send the native view size that was used during the most recent
1610 draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
1611 if (mSettings.getUseWideViewPort()) {
1612 draw.mMinPrefWidth = Math.max(
1613 mViewportWidth == -1 ? DEFAULT_VIEWPORT_WIDTH
1614 : (mViewportWidth == 0 ? mCurrentViewWidth
1616 nativeGetContentMinPrefWidth());
1618 if (mRestoreState != null) {
1619 draw.mRestoreState = mRestoreState;
1620 mRestoreState = null;
1622 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
1623 Message.obtain(mWebView.mPrivateHandler,
1624 WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
1625 if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
1626 // as we have the new picture, try to sync the scroll position
1627 Message.obtain(mWebView.mPrivateHandler,
1628 WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX,
1629 mWebkitScrollY).sendToTarget();
1630 mWebkitScrollX = mWebkitScrollY = 0;
1635 ///////////////////////////////////////////////////////////////////////////
1636 // These are called from the UI thread, not our thread
1638 static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
1640 Paint.SUBPIXEL_TEXT_FLAG;
1641 static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
1644 final DrawFilter mZoomFilter =
1645 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
1647 /* package */ void drawContentPicture(Canvas canvas, int color,
1648 boolean animatingZoom,
1649 boolean animatingScroll) {
1650 DrawFilter df = null;
1651 if (animatingZoom) {
1653 } else if (animatingScroll) {
1656 canvas.setDrawFilter(df);
1657 boolean tookTooLong = nativeDrawContent(canvas, color);
1658 canvas.setDrawFilter(null);
1659 if (tookTooLong && mSplitPictureIsScheduled == false) {
1660 mSplitPictureIsScheduled = true;
1661 sendMessage(EventHub.SPLIT_PICTURE_SET);
1665 /* package */ synchronized boolean pictureReady() {
1666 return 0 != mNativeClass ? nativePictureReady() : false;
1669 /*package*/ synchronized Picture copyContentPicture() {
1670 Picture result = new Picture();
1671 if (0 != mNativeClass) {
1672 nativeCopyContentToPicture(result);
1677 static void pauseUpdate(WebViewCore core) {
1678 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1679 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1680 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1681 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1682 .obtainMessage(WebCoreThread.REDUCE_PRIORITY));
1683 // Note: there is one possible failure mode. If pauseUpdate() is called
1684 // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
1685 // of the queue and about to be executed. mDrawIsScheduled may be set to
1686 // false in webkitDraw(). So update won't be blocked. But at least the
1687 // webcore thread priority is still lowered.
1689 synchronized (core) {
1690 core.mDrawIsPaused = true;
1691 core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
1696 static void resumeUpdate(WebViewCore core) {
1697 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages
1698 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY);
1699 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY);
1700 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
1701 .obtainMessage(WebCoreThread.RESUME_PRIORITY));
1703 synchronized (core) {
1704 core.mDrawIsScheduled = false;
1705 core.mDrawIsPaused = false;
1706 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate");
1712 static void startCacheTransaction() {
1713 sWebCoreHandler.sendMessage(sWebCoreHandler
1714 .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER));
1717 static void endCacheTransaction() {
1718 sWebCoreHandler.sendMessage(sWebCoreHandler
1719 .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER));
1722 //////////////////////////////////////////////////////////////////////////
1724 private void restoreState(int index) {
1725 WebBackForwardList list = mCallbackProxy.getBackForwardList();
1726 int size = list.getSize();
1727 for (int i = 0; i < size; i++) {
1728 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame);
1730 mBrowserFrame.mLoadInitFromJava = true;
1731 list.restoreIndex(mBrowserFrame.mNativeFrame, index);
1732 mBrowserFrame.mLoadInitFromJava = false;
1735 //-------------------------------------------------------------------------
1736 // Implement abstract methods in WebViewCore, native WebKit callback part
1737 //-------------------------------------------------------------------------
1739 // called from JNI or WebView thread
1740 /* package */ void contentDraw() {
1741 // don't update the Picture until we have an initial width and finish
1743 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) {
1746 // only fire an event if this is our first request
1747 synchronized (this) {
1748 if (mDrawIsPaused || mDrawIsScheduled) {
1751 mDrawIsScheduled = true;
1752 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
1757 private void contentScrollBy(int dx, int dy, boolean animate) {
1758 if (!mBrowserFrame.firstLayoutDone()) {
1759 // Will this happen? If yes, we need to do something here.
1762 if (mWebView != null) {
1763 Message msg = Message.obtain(mWebView.mPrivateHandler,
1764 WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
1765 if (mDrawIsScheduled) {
1766 mEventHub.sendMessage(Message.obtain(null,
1767 EventHub.MESSAGE_RELAY, msg));
1775 private void contentScrollTo(int x, int y) {
1776 if (!mBrowserFrame.firstLayoutDone()) {
1778 * WebKit restore state will be called before didFirstLayout(),
1779 * remember the position as it has to be applied after restoring
1780 * zoom factor which is controlled by screenWidth.
1786 if (mWebView != null) {
1787 Message msg = Message.obtain(mWebView.mPrivateHandler,
1788 WebView.SCROLL_TO_MSG_ID, x, y);
1789 if (mDrawIsScheduled) {
1790 mEventHub.sendMessage(Message.obtain(null,
1791 EventHub.MESSAGE_RELAY, msg));
1799 private void contentSpawnScrollTo(int x, int y) {
1800 if (!mBrowserFrame.firstLayoutDone()) {
1802 * WebKit restore state will be called before didFirstLayout(),
1803 * remember the position as it has to be applied after restoring
1804 * zoom factor which is controlled by screenWidth.
1810 if (mWebView != null) {
1811 Message msg = Message.obtain(mWebView.mPrivateHandler,
1812 WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
1813 if (mDrawIsScheduled) {
1814 mEventHub.sendMessage(Message.obtain(null,
1815 EventHub.MESSAGE_RELAY, msg));
1823 private void sendNotifyProgressFinished() {
1824 sendUpdateTextEntry();
1825 // as CacheManager can behave based on database transaction, we need to
1826 // call tick() to trigger endTransaction
1827 sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER);
1828 sWebCoreHandler.sendMessage(sWebCoreHandler
1829 .obtainMessage(WebCoreThread.CACHE_TICKER));
1833 /* Called by JNI. The coordinates are in doc coordinates, so they need to
1834 be scaled before they can be used by the view system, which happens
1835 in WebView since it (and its thread) know the current scale factor.
1837 private void sendViewInvalidate(int left, int top, int right, int bottom) {
1838 if (mWebView != null) {
1839 Message.obtain(mWebView.mPrivateHandler,
1840 WebView.INVAL_RECT_MSG_ID,
1841 new Rect(left, top, right, bottom)).sendToTarget();
1845 /* package */ WebView getWebView() {
1849 private native void setViewportSettingsFromNative();
1852 private void didFirstLayout(boolean standardLoad) {
1853 if (DebugFlags.WEB_VIEW_CORE) {
1854 Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
1857 mBrowserFrame.didFirstLayout();
1859 if (mWebView == null) return;
1861 setupViewport(standardLoad || mRestoredScale > 0);
1863 // reset the scroll position, the restored offset and scales
1864 mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY
1865 = mRestoredScale = mRestoredScreenWidthScale = 0;
1869 private void updateViewport() {
1870 // if updateViewport is called before first layout, wait until first
1871 // layout to update the viewport. In the rare case, this is called after
1872 // first layout, force an update as we have just parsed the viewport
1874 if (mBrowserFrame.firstLayoutDone()) {
1875 setupViewport(true);
1879 private void setupViewport(boolean updateRestoreState) {
1880 // set the viewport settings from WebKit
1881 setViewportSettingsFromNative();
1883 // adjust the default scale to match the densityDpi
1884 float adjust = 1.0f;
1885 if (mViewportDensityDpi == -1) {
1886 if (WebView.DEFAULT_SCALE_PERCENT != 100) {
1887 adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f;
1889 } else if (mViewportDensityDpi > 0) {
1890 adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi
1891 / mViewportDensityDpi;
1893 int defaultScale = (int) (adjust * 100);
1895 if (mViewportInitialScale > 0) {
1896 mViewportInitialScale *= adjust;
1898 if (mViewportMinimumScale > 0) {
1899 mViewportMinimumScale *= adjust;
1901 if (mViewportMaximumScale > 0) {
1902 mViewportMaximumScale *= adjust;
1905 // infer the values if they are not defined.
1906 if (mViewportWidth == 0) {
1907 if (mViewportInitialScale == 0) {
1908 mViewportInitialScale = defaultScale;
1911 if (mViewportUserScalable == false) {
1912 mViewportInitialScale = defaultScale;
1913 mViewportMinimumScale = defaultScale;
1914 mViewportMaximumScale = defaultScale;
1916 if (mViewportMinimumScale > mViewportInitialScale
1917 && mViewportInitialScale != 0) {
1918 mViewportMinimumScale = mViewportInitialScale;
1920 if (mViewportMaximumScale > 0
1921 && mViewportMaximumScale < mViewportInitialScale) {
1922 mViewportMaximumScale = mViewportInitialScale;
1924 if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) {
1928 // if mViewportWidth is 0, it means device-width, always update.
1929 if (mViewportWidth != 0 && !updateRestoreState) return;
1931 // now notify webview
1932 // webViewWidth refers to the width in the view system
1934 // viewportWidth refers to the width in the document system
1935 int viewportWidth = mCurrentViewWidth;
1936 if (viewportWidth == 0) {
1937 // this may happen when WebView just starts. This is not perfect as
1938 // we call WebView method from WebCore thread. But not perfect
1939 // reference is better than no reference.
1940 webViewWidth = mWebView.getViewWidth();
1941 viewportWidth = (int) (webViewWidth / adjust);
1942 if (viewportWidth == 0) {
1943 Log.w(LOGTAG, "Can't get the viewWidth after the first layout");
1946 webViewWidth = Math.round(viewportWidth * mCurrentViewScale);
1948 mRestoreState = new RestoreState();
1949 mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
1950 mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
1951 mRestoreState.mDefaultScale = adjust;
1952 mRestoreState.mScrollX = mRestoredX;
1953 mRestoreState.mScrollY = mRestoredY;
1954 mRestoreState.mMobileSite = (0 == mViewportWidth);
1955 if (mRestoredScale > 0) {
1956 if (mRestoredScreenWidthScale > 0) {
1957 mRestoreState.mTextWrapScale =
1958 mRestoredScreenWidthScale / 100.0f;
1959 // 0 will trigger WebView to turn on zoom overview mode
1960 mRestoreState.mViewScale = 0;
1962 mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1963 mRestoredScale / 100.0f;
1966 if (mViewportInitialScale > 0) {
1967 mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1968 mViewportInitialScale / 100.0f;
1969 } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
1970 mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
1971 (float) webViewWidth / mViewportWidth;
1973 mRestoreState.mTextWrapScale = adjust;
1974 // 0 will trigger WebView to turn on zoom overview mode
1975 mRestoreState.mViewScale = 0;
1979 if (mWebView.mHeightCanMeasure) {
1980 // Trick to ensure that the Picture has the exact height for the
1981 // content by forcing to layout with 0 height after the page is
1982 // ready, which is indicated by didFirstLayout. This is essential to
1983 // get rid of the white space in the GMail which uses WebView for
1985 mWebView.mLastHeightSent = 0;
1986 // Send a negative scale to indicate that WebCore should reuse
1987 // the current scale
1988 WebView.ViewSizeData data = new WebView.ViewSizeData();
1989 data.mWidth = mWebView.mLastWidthSent;
1991 // if mHeightCanMeasure is true, getUseWideViewPort() can't be
1992 // true. It is safe to use mWidth for mTextWrapWidth.
1993 data.mTextWrapWidth = data.mWidth;
1994 data.mScale = -1.0f;
1995 data.mIgnoreHeight = false;
1996 // send VIEW_SIZE_CHANGED to the front of the queue so that we can
1997 // avoid pushing the wrong picture to the WebView side. If there is
1998 // a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
1999 // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED
2000 // in the queue, as mLastHeightSent has been updated here, we may
2001 // miss the requestLayout in WebView side after the new picture.
2002 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2003 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2004 EventHub.VIEW_SIZE_CHANGED, data));
2005 } else if (mSettings.getUseWideViewPort()) {
2006 if (viewportWidth == 0) {
2007 // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
2009 mWebView.mLastWidthSent = 0;
2011 WebView.ViewSizeData data = new WebView.ViewSizeData();
2012 // mViewScale as 0 means it is in zoom overview mode. So we don't
2013 // know the exact scale. If mRestoredScale is non-zero, use it;
2014 // otherwise just use mTextWrapScale as the initial scale.
2015 data.mScale = mRestoreState.mViewScale == 0
2016 ? (mRestoredScale > 0 ? mRestoredScale
2017 : mRestoreState.mTextWrapScale)
2018 : mRestoreState.mViewScale;
2019 data.mWidth = Math.round(webViewWidth / data.mScale);
2020 data.mHeight = mCurrentViewHeight * data.mWidth / viewportWidth;
2021 data.mTextWrapWidth = Math.round(webViewWidth
2022 / mRestoreState.mTextWrapScale);
2023 data.mIgnoreHeight = false;
2024 // send VIEW_SIZE_CHANGED to the front of the queue so that we
2025 // can avoid pushing the wrong picture to the WebView side.
2026 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
2027 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
2028 EventHub.VIEW_SIZE_CHANGED, data));
2034 private void restoreScale(int scale) {
2035 if (mBrowserFrame.firstLayoutDone() == false) {
2036 mRestoredScale = scale;
2041 private void restoreScreenWidthScale(int scale) {
2042 if (!mSettings.getUseWideViewPort()) {
2046 if (mBrowserFrame.firstLayoutDone() == false) {
2047 mRestoredScreenWidthScale = scale;
2052 private void needTouchEvents(boolean need) {
2053 if (mWebView != null) {
2054 Message.obtain(mWebView.mPrivateHandler,
2055 WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0)
2061 private void updateTextfield(int ptr, boolean changeToPassword,
2062 String text, int textGeneration) {
2063 if (mWebView != null) {
2064 Message msg = Message.obtain(mWebView.mPrivateHandler,
2065 WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
2066 textGeneration, text);
2067 msg.getData().putBoolean("password", changeToPassword);
2073 private void updateTextSelection(int pointer, int start, int end,
2074 int textGeneration) {
2075 if (mWebView != null) {
2076 Message.obtain(mWebView.mPrivateHandler,
2077 WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
2078 new TextSelectionData(start, end)).sendToTarget();
2083 private void clearTextEntry() {
2084 if (mWebView == null) return;
2085 Message.obtain(mWebView.mPrivateHandler,
2086 WebView.CLEAR_TEXT_ENTRY).sendToTarget();
2089 private native void nativeUpdateFrameCacheIfLoading();
2092 * Scroll the focused textfield to (xPercent, y) in document space
2094 private native void nativeScrollFocusedTextInput(float xPercent, int y);
2096 // these must be in document space (i.e. not scaled/zoomed).
2097 private native void nativeSetScrollOffset(int gen, int dx, int dy);
2099 private native void nativeSetGlobalBounds(int x, int y, int w, int h);
2102 private void requestListBox(String[] array, boolean[] enabledArray,
2103 int[] selectedArray) {
2104 if (mWebView != null) {
2105 mWebView.requestListBox(array, enabledArray, selectedArray);
2110 private void requestListBox(String[] array, boolean[] enabledArray,
2112 if (mWebView != null) {
2113 mWebView.requestListBox(array, enabledArray, selection);
2119 private void requestKeyboard(boolean showKeyboard) {
2120 if (mWebView != null) {
2121 Message.obtain(mWebView.mPrivateHandler,
2122 WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
2127 // called by JNI. PluginWidget function to launch an activity and overlays
2128 // the activity with the View provided by the plugin class.
2129 private void startFullScreenPluginActivity(String libName, String clsName, int npp) {
2130 if (mWebView == null) {
2134 String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName);
2135 if (pkgName == null) {
2136 Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2140 Intent intent = new Intent("android.intent.webkit.PLUGIN");
2141 intent.putExtra(PluginActivity.INTENT_EXTRA_PACKAGE_NAME, pkgName);
2142 intent.putExtra(PluginActivity.INTENT_EXTRA_CLASS_NAME, clsName);
2143 intent.putExtra(PluginActivity.INTENT_EXTRA_NPP_INSTANCE, npp);
2144 mWebView.getContext().startActivity(intent);
2147 // called by JNI. PluginWidget functions for creating an embedded View for
2148 // the surface drawing model.
2149 private ViewManager.ChildView createSurface(String libName, String clsName,
2150 int npp, int x, int y, int width, int height) {
2151 if (mWebView == null) {
2155 String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName);
2156 if (pkgName == null) {
2157 Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK");
2161 PluginStub stub =PluginUtil.getPluginStub(mWebView.getContext(),pkgName, clsName);
2163 Log.e(LOGTAG, "Unable to find plugin class (" + clsName +
2164 ") in the apk (" + pkgName + ")");
2168 View pluginView = stub.getEmbeddedView(npp, mWebView.getContext());
2170 ViewManager.ChildView view = mWebView.mViewManager.createView();
2171 view.mView = pluginView;
2172 view.attachView(x, y, width, height);
2176 private void destroySurface(ViewManager.ChildView childView) {
2177 childView.removeView();
2180 private native void nativePause();
2181 private native void nativeResume();
2182 private native void nativeFreeMemory();