WebViewCore.java revision 0ac031b3d29c6de90895c875991585812dc7388f
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.webkit; 18 19import android.content.Context; 20import android.graphics.Canvas; 21import android.graphics.DrawFilter; 22import android.graphics.Paint; 23import android.graphics.PaintFlagsDrawFilter; 24import android.graphics.Picture; 25import android.graphics.Point; 26import android.graphics.Rect; 27import android.graphics.Region; 28import android.os.Handler; 29import android.os.Looper; 30import android.os.Message; 31import android.os.Process; 32import android.util.Log; 33import android.util.SparseBooleanArray; 34import android.view.KeyEvent; 35import android.view.SurfaceHolder; 36import android.view.SurfaceView; 37 38import java.util.ArrayList; 39import java.util.Set; 40 41import junit.framework.Assert; 42 43final class WebViewCore { 44 45 private static final String LOGTAG = "webcore"; 46 47 static { 48 // Load libwebcore during static initialization. This happens in the 49 // zygote process so it will be shared read-only across all app 50 // processes. 51 System.loadLibrary("webcore"); 52 } 53 54 /* 55 * WebViewCore always executes in the same thread as the native webkit. 56 */ 57 58 // The WebView that corresponds to this WebViewCore. 59 private WebView mWebView; 60 // Proxy for handling callbacks from native code 61 private final CallbackProxy mCallbackProxy; 62 // Settings object for maintaining all settings 63 private final WebSettings mSettings; 64 // Context for initializing the BrowserFrame with the proper assets. 65 private final Context mContext; 66 // The pointer to a native view object. 67 private int mNativeClass; 68 // The BrowserFrame is an interface to the native Frame component. 69 private BrowserFrame mBrowserFrame; 70 71 /* 72 * range is from 200 to 10,000. 0 is a special value means device-width. -1 73 * means undefined. 74 */ 75 private int mViewportWidth = -1; 76 77 /* 78 * range is from 200 to 10,000. 0 is a special value means device-height. -1 79 * means undefined. 80 */ 81 private int mViewportHeight = -1; 82 83 /* 84 * scale in percent, range is from 1 to 1000. 0 means undefined. 85 */ 86 private int mViewportInitialScale = 0; 87 88 /* 89 * scale in percent, range is from 1 to 1000. 0 means undefined. 90 */ 91 private int mViewportMinimumScale = 0; 92 93 /* 94 * scale in percent, range is from 1 to 1000. 0 means undefined. 95 */ 96 private int mViewportMaximumScale = 0; 97 98 private boolean mViewportUserScalable = true; 99 100 private int mRestoredScale = 0; 101 private int mRestoredScreenWidthScale = 0; 102 private int mRestoredX = 0; 103 private int mRestoredY = 0; 104 105 private int mWebkitScrollX = 0; 106 private int mWebkitScrollY = 0; 107 108 // The thread name used to identify the WebCore thread and for use in 109 // debugging other classes that require operation within the WebCore thread. 110 /* package */ static final String THREAD_NAME = "WebViewCoreThread"; 111 112 public WebViewCore(Context context, WebView w, CallbackProxy proxy) { 113 // No need to assign this in the WebCore thread. 114 mCallbackProxy = proxy; 115 mWebView = w; 116 // This context object is used to initialize the WebViewCore during 117 // subwindow creation. 118 mContext = context; 119 120 // We need to wait for the initial thread creation before sending 121 // a message to the WebCore thread. 122 // XXX: This is the only time the UI thread will wait for the WebCore 123 // thread! 124 synchronized (WebViewCore.class) { 125 if (sWebCoreHandler == null) { 126 // Create a global thread and start it. 127 Thread t = new Thread(new WebCoreThread()); 128 t.setName(THREAD_NAME); 129 t.start(); 130 try { 131 WebViewCore.class.wait(); 132 } catch (InterruptedException e) { 133 Log.e(LOGTAG, "Caught exception while waiting for thread " + 134 "creation."); 135 Log.e(LOGTAG, Log.getStackTraceString(e)); 136 } 137 } 138 } 139 // Create an EventHub to handle messages before and after the thread is 140 // ready. 141 mEventHub = new EventHub(); 142 // Create a WebSettings object for maintaining all settings 143 mSettings = new WebSettings(mContext, mWebView); 144 // The WebIconDatabase needs to be initialized within the UI thread so 145 // just request the instance here. 146 WebIconDatabase.getInstance(); 147 // Create the WebStorage singleton 148 WebStorage.getInstance(); 149 // Send a message to initialize the WebViewCore. 150 Message init = sWebCoreHandler.obtainMessage( 151 WebCoreThread.INITIALIZE, this); 152 sWebCoreHandler.sendMessage(init); 153 } 154 155 /* Initialize private data within the WebCore thread. 156 */ 157 private void initialize() { 158 /* Initialize our private BrowserFrame class to handle all 159 * frame-related functions. We need to create a new view which 160 * in turn creates a C level FrameView and attaches it to the frame. 161 */ 162 mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy, 163 mSettings); 164 // Sync the native settings and also create the WebCore thread handler. 165 mSettings.syncSettingsAndCreateHandler(mBrowserFrame); 166 // Create the handler and transfer messages for the IconDatabase 167 WebIconDatabase.getInstance().createHandler(); 168 // Create the handler for WebStorage 169 WebStorage.getInstance().createHandler(); 170 // Create the handler for GeolocationPermissions. 171 GeolocationPermissions.getInstance().createHandler(); 172 // The transferMessages call will transfer all pending messages to the 173 // WebCore thread handler. 174 mEventHub.transferMessages(); 175 176 // Send a message back to WebView to tell it that we have set up the 177 // WebCore thread. 178 if (mWebView != null) { 179 Message.obtain(mWebView.mPrivateHandler, 180 WebView.WEBCORE_INITIALIZED_MSG_ID, 181 mNativeClass, 0).sendToTarget(); 182 } 183 184 } 185 186 /* Handle the initialization of WebViewCore during subwindow creation. This 187 * method is called from the WebCore thread but it is called before the 188 * INITIALIZE message can be handled. 189 */ 190 /* package */ void initializeSubwindow() { 191 // Go ahead and initialize the core components. 192 initialize(); 193 // Remove the INITIALIZE method so we don't try to initialize twice. 194 sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this); 195 } 196 197 /* Get the BrowserFrame component. This is used for subwindow creation and 198 * is called only from BrowserFrame in the WebCore thread. */ 199 /* package */ BrowserFrame getBrowserFrame() { 200 return mBrowserFrame; 201 } 202 203 //------------------------------------------------------------------------- 204 // Common methods 205 //------------------------------------------------------------------------- 206 207 /** 208 * Causes all timers to pause. This applies to all WebViews in the current 209 * app process. 210 */ 211 public static void pauseTimers() { 212 if (BrowserFrame.sJavaBridge == null) { 213 throw new IllegalStateException( 214 "No WebView has been created in this process!"); 215 } 216 BrowserFrame.sJavaBridge.pause(); 217 } 218 219 /** 220 * Resume all timers. This applies to all WebViews in the current process. 221 */ 222 public static void resumeTimers() { 223 if (BrowserFrame.sJavaBridge == null) { 224 throw new IllegalStateException( 225 "No WebView has been created in this process!"); 226 } 227 BrowserFrame.sJavaBridge.resume(); 228 } 229 230 public WebSettings getSettings() { 231 return mSettings; 232 } 233 234 /** 235 * Add an error message to the client's console. 236 * @param message The message to add 237 * @param lineNumber the line on which the error occurred 238 * @param sourceID the filename of the source that caused the error. 239 */ 240 protected void addMessageToConsole(String message, int lineNumber, String sourceID) { 241 mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID); 242 } 243 244 /** 245 * Invoke a javascript alert. 246 * @param message The message displayed in the alert. 247 */ 248 protected void jsAlert(String url, String message) { 249 mCallbackProxy.onJsAlert(url, message); 250 } 251 252 /** 253 * Notify the browser that the origin has exceeded it's database quota. 254 * @param url The URL that caused the overflow. 255 * @param databaseIdentifier The identifier of the database. 256 * @param currentQuota The current quota for the origin. 257 */ 258 protected void exceededDatabaseQuota(String url, 259 String databaseIdentifier, 260 long currentQuota) { 261 // Inform the callback proxy of the quota overflow. Send an object 262 // that encapsulates a call to the nativeSetDatabaseQuota method to 263 // awaken the sleeping webcore thread when a decision from the 264 // client to allow or deny quota is available. 265 mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier, 266 currentQuota, getUsedQuota(), new WebStorage.QuotaUpdater() { 267 public void updateQuota(long quota) { 268 nativeSetNewStorageLimit(quota); 269 } 270 }); 271 } 272 273 /** 274 * Notify the browser that the appcache has exceeded its max size. 275 * @param spaceNeeded is the amount of disk space that would be needed 276 * in order for the last appcache operation to succeed. 277 */ 278 protected void reachedMaxAppCacheSize(long spaceNeeded) { 279 mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(), 280 new WebStorage.QuotaUpdater() { 281 public void updateQuota(long quota) { 282 nativeSetNewStorageLimit(quota); 283 } 284 }); 285 } 286 287 /** 288 * Invoke a javascript confirm dialog. 289 * @param message The message displayed in the dialog. 290 * @return True if the user confirmed or false if the user cancelled. 291 */ 292 protected boolean jsConfirm(String url, String message) { 293 return mCallbackProxy.onJsConfirm(url, message); 294 } 295 296 /** 297 * Invoke a javascript prompt dialog. 298 * @param message The message to be displayed in the dialog. 299 * @param defaultValue The default value in the prompt input. 300 * @return The input from the user or null to indicate the user cancelled 301 * the dialog. 302 */ 303 protected String jsPrompt(String url, String message, String defaultValue) { 304 return mCallbackProxy.onJsPrompt(url, message, defaultValue); 305 } 306 307 /** 308 * Invoke a javascript before unload dialog. 309 * @param url The url that is requesting the dialog. 310 * @param message The message displayed in the dialog. 311 * @return True if the user confirmed or false if the user cancelled. False 312 * will cancel the navigation. 313 */ 314 protected boolean jsUnload(String url, String message) { 315 return mCallbackProxy.onJsBeforeUnload(url, message); 316 } 317 318 /** 319 * 320 * Callback to notify that a JavaScript execution timeout has occured. 321 * @return True if the JavaScript execution should be interrupted. False 322 * will continue the execution. 323 */ 324 protected boolean jsInterrupt() { 325 return mCallbackProxy.onJsTimeout(); 326 } 327 328 //------------------------------------------------------------------------- 329 // JNI methods 330 //------------------------------------------------------------------------- 331 332 static native String nativeFindAddress(String addr, boolean caseInsensitive); 333 334 /** 335 * Empty the picture set. 336 */ 337 private native void nativeClearContent(); 338 339 /** 340 * Create a flat picture from the set of pictures. 341 */ 342 private native void nativeCopyContentToPicture(Picture picture); 343 344 /** 345 * Draw the picture set with a background color. Returns true 346 * if some individual picture took too long to draw and can be 347 * split into parts. Called from the UI thread. 348 */ 349 private native boolean nativeDrawContent(Canvas canvas, int color); 350 351 /** 352 * check to see if picture is blank and in progress 353 */ 354 private native boolean nativePictureReady(); 355 356 /** 357 * Redraw a portion of the picture set. The Point wh returns the 358 * width and height of the overall picture. 359 */ 360 private native boolean nativeRecordContent(Region invalRegion, Point wh); 361 362 /** 363 * Splits slow parts of the picture set. Called from the webkit 364 * thread after nativeDrawContent returns true. 365 */ 366 private native void nativeSplitContent(); 367 368 private native boolean nativeKey(int keyCode, int unichar, 369 int repeatCount, boolean isShift, boolean isAlt, boolean isSym, 370 boolean isDown); 371 372 private native void nativeClick(int framePtr, int nodePtr); 373 374 private native void nativeSendListBoxChoices(boolean[] choices, int size); 375 376 private native void nativeSendListBoxChoice(int choice); 377 378 /* Tell webkit what its width and height are, for the purposes 379 of layout/line-breaking. These coordinates are in document space, 380 which is the same as View coords unless we have zoomed the document 381 (see nativeSetZoom). 382 screenWidth is used by layout to wrap column around. If viewport uses 383 fixed size, screenWidth can be different from width with zooming. 384 should this be called nativeSetViewPortSize? 385 */ 386 private native void nativeSetSize(int width, int height, int screenWidth, 387 float scale, int realScreenWidth, int screenHeight); 388 389 private native int nativeGetContentMinPrefWidth(); 390 391 // Start: functions that deal with text editing 392 private native void nativeReplaceTextfieldText( 393 int oldStart, int oldEnd, String replace, int newStart, int newEnd, 394 int textGeneration); 395 396 private native void passToJs(int gen, 397 String currentText, int keyCode, int keyValue, boolean down, 398 boolean cap, boolean fn, boolean sym); 399 400 private native void nativeSetFocusControllerActive(boolean active); 401 402 private native void nativeSaveDocumentState(int frame); 403 404 private native void nativeMoveMouse(int framePtr, int x, int y); 405 406 private native void nativeMoveMouseIfLatest(int moveGeneration, 407 int framePtr, int x, int y); 408 409 private native String nativeRetrieveHref(int framePtr, int nodePtr); 410 411 private native void nativeTouchUp(int touchGeneration, 412 int framePtr, int nodePtr, int x, int y, 413 int size); 414 415 private native boolean nativeHandleTouchEvent(int action, int x, int y); 416 417 private native void nativeUpdateFrameCache(); 418 419 private native void nativeSetSnapAnchor(int x, int y); 420 421 private native void nativeSnapToAnchor(); 422 423 private native void nativeSetBackgroundColor(int color); 424 425 private native void nativeDumpDomTree(boolean useFile); 426 427 private native void nativeDumpRenderTree(boolean useFile); 428 429 private native void nativeDumpNavTree(); 430 431 private native void nativeSetJsFlags(String flags); 432 433 /** 434 * Delete text from start to end in the focused textfield. If there is no 435 * focus, or if start == end, silently fail. If start and end are out of 436 * order, swap them. 437 * @param start Beginning of selection to delete. 438 * @param end End of selection to delete. 439 * @param textGeneration Text generation number when delete was pressed. 440 */ 441 private native void nativeDeleteSelection(int start, int end, 442 int textGeneration); 443 444 /** 445 * Set the selection to (start, end) in the focused textfield. If start and 446 * end are out of order, swap them. 447 * @param start Beginning of selection. 448 * @param end End of selection. 449 */ 450 private native void nativeSetSelection(int start, int end); 451 452 private native String nativeGetSelection(Region sel); 453 454 // Register a scheme to be treated as local scheme so that it can access 455 // local asset files for resources 456 private native void nativeRegisterURLSchemeAsLocal(String scheme); 457 458 /* 459 * Inform webcore that the user has decided whether to allow or deny new 460 * quota for the current origin or more space for the app cache, and that 461 * the main thread should wake up now. 462 * @param limit Is the new quota for an origin or new app cache max size. 463 */ 464 private native void nativeSetNewStorageLimit(long limit); 465 466 private native void nativeUpdatePluginState(int framePtr, int nodePtr, int state); 467 468 // EventHub for processing messages 469 private final EventHub mEventHub; 470 // WebCore thread handler 471 private static Handler sWebCoreHandler; 472 // Class for providing Handler creation inside the WebCore thread. 473 private static class WebCoreThread implements Runnable { 474 // Message id for initializing a new WebViewCore. 475 private static final int INITIALIZE = 0; 476 private static final int REDUCE_PRIORITY = 1; 477 private static final int RESUME_PRIORITY = 2; 478 private static final int CACHE_TICKER = 3; 479 private static final int BLOCK_CACHE_TICKER = 4; 480 private static final int RESUME_CACHE_TICKER = 5; 481 482 private static final int CACHE_TICKER_INTERVAL = 60 * 1000; // 1 minute 483 484 private static boolean mCacheTickersBlocked = true; 485 486 public void run() { 487 Looper.prepare(); 488 Assert.assertNull(sWebCoreHandler); 489 synchronized (WebViewCore.class) { 490 sWebCoreHandler = new Handler() { 491 @Override 492 public void handleMessage(Message msg) { 493 switch (msg.what) { 494 case INITIALIZE: 495 WebViewCore core = (WebViewCore) msg.obj; 496 core.initialize(); 497 break; 498 499 case REDUCE_PRIORITY: 500 // 3 is an adjustable number. 501 Process.setThreadPriority( 502 Process.THREAD_PRIORITY_DEFAULT + 3 * 503 Process.THREAD_PRIORITY_LESS_FAVORABLE); 504 break; 505 506 case RESUME_PRIORITY: 507 Process.setThreadPriority( 508 Process.THREAD_PRIORITY_DEFAULT); 509 break; 510 511 case CACHE_TICKER: 512 if (!mCacheTickersBlocked) { 513 CacheManager.endCacheTransaction(); 514 CacheManager.startCacheTransaction(); 515 sendMessageDelayed( 516 obtainMessage(CACHE_TICKER), 517 CACHE_TICKER_INTERVAL); 518 } 519 break; 520 521 case BLOCK_CACHE_TICKER: 522 if (CacheManager.endCacheTransaction()) { 523 mCacheTickersBlocked = true; 524 } 525 break; 526 527 case RESUME_CACHE_TICKER: 528 if (CacheManager.startCacheTransaction()) { 529 mCacheTickersBlocked = false; 530 } 531 break; 532 } 533 } 534 }; 535 WebViewCore.class.notify(); 536 } 537 Looper.loop(); 538 } 539 } 540 541 static class BaseUrlData { 542 String mBaseUrl; 543 String mData; 544 String mMimeType; 545 String mEncoding; 546 String mFailUrl; 547 } 548 549 static class CursorData { 550 CursorData() {} 551 CursorData(int frame, int node, int x, int y) { 552 mFrame = frame; 553 mX = x; 554 mY = y; 555 } 556 int mMoveGeneration; 557 int mFrame; 558 int mX; 559 int mY; 560 } 561 562 static class JSInterfaceData { 563 Object mObject; 564 String mInterfaceName; 565 } 566 567 static class JSKeyData { 568 String mCurrentText; 569 KeyEvent mEvent; 570 } 571 572 static class PostUrlData { 573 String mUrl; 574 byte[] mPostData; 575 } 576 577 static class DeleteSelectionData { 578 int mStart; 579 int mEnd; 580 int mTextGeneration; 581 } 582 583 static class ReplaceTextData { 584 String mReplace; 585 int mNewStart; 586 int mNewEnd; 587 int mTextGeneration; 588 } 589 590 static class TouchUpData { 591 int mMoveGeneration; 592 int mFrame; 593 int mNode; 594 int mX; 595 int mY; 596 int mSize; 597 } 598 599 static class TouchEventData { 600 int mAction; // MotionEvent.getAction() 601 int mX; 602 int mY; 603 } 604 605 static class PluginStateData { 606 int mFrame; 607 int mNode; 608 int mState; 609 } 610 611 static final String[] HandlerDebugString = { 612 "SCROLL_TEXT_INPUT", // = 99 613 "LOAD_URL", // = 100; 614 "STOP_LOADING", // = 101; 615 "RELOAD", // = 102; 616 "KEY_DOWN", // = 103; 617 "KEY_UP", // = 104; 618 "VIEW_SIZE_CHANGED", // = 105; 619 "GO_BACK_FORWARD", // = 106; 620 "SET_SCROLL_OFFSET", // = 107; 621 "RESTORE_STATE", // = 108; 622 "PAUSE_TIMERS", // = 109; 623 "RESUME_TIMERS", // = 110; 624 "CLEAR_CACHE", // = 111; 625 "CLEAR_HISTORY", // = 112; 626 "SET_SELECTION", // = 113; 627 "REPLACE_TEXT", // = 114; 628 "PASS_TO_JS", // = 115; 629 "SET_GLOBAL_BOUNDS", // = 116; 630 "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117; 631 "CLICK", // = 118; 632 "SET_NETWORK_STATE", // = 119; 633 "DOC_HAS_IMAGES", // = 120; 634 "SET_SNAP_ANCHOR", // = 121; 635 "DELETE_SELECTION", // = 122; 636 "LISTBOX_CHOICES", // = 123; 637 "SINGLE_LISTBOX_CHOICE", // = 124; 638 "MESSAGE_RELAY", // = 125; 639 "SET_BACKGROUND_COLOR", // = 126; 640 "PLUGIN_STATE", // = 127; 641 "SAVE_DOCUMENT_STATE", // = 128; 642 "GET_SELECTION", // = 129; 643 "WEBKIT_DRAW", // = 130; 644 "SYNC_SCROLL", // = 131; 645 "POST_URL", // = 132; 646 "SPLIT_PICTURE_SET", // = 133; 647 "CLEAR_CONTENT", // = 134; 648 "SET_MOVE_MOUSE", // = 135; 649 "SET_MOVE_MOUSE_IF_LATEST", // = 136; 650 "REQUEST_CURSOR_HREF", // = 137; 651 "ADD_JS_INTERFACE", // = 138; 652 "LOAD_DATA", // = 139; 653 "TOUCH_UP", // = 140; 654 "TOUCH_EVENT", // = 141; 655 "SET_ACTIVE", // = 142; 656 "ON_PAUSE", // = 143 657 "ON_RESUME", // = 144 658 "FREE_MEMORY", // = 145 659 }; 660 661 class EventHub { 662 // Message Ids 663 static final int SCROLL_TEXT_INPUT = 99; 664 static final int LOAD_URL = 100; 665 static final int STOP_LOADING = 101; 666 static final int RELOAD = 102; 667 static final int KEY_DOWN = 103; 668 static final int KEY_UP = 104; 669 static final int VIEW_SIZE_CHANGED = 105; 670 static final int GO_BACK_FORWARD = 106; 671 static final int SET_SCROLL_OFFSET = 107; 672 static final int RESTORE_STATE = 108; 673 static final int PAUSE_TIMERS = 109; 674 static final int RESUME_TIMERS = 110; 675 static final int CLEAR_CACHE = 111; 676 static final int CLEAR_HISTORY = 112; 677 static final int SET_SELECTION = 113; 678 static final int REPLACE_TEXT = 114; 679 static final int PASS_TO_JS = 115; 680 static final int SET_GLOBAL_BOUNDS = 116; 681 static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117; 682 static final int CLICK = 118; 683 static final int SET_NETWORK_STATE = 119; 684 static final int DOC_HAS_IMAGES = 120; 685 static final int SET_SNAP_ANCHOR = 121; 686 static final int DELETE_SELECTION = 122; 687 static final int LISTBOX_CHOICES = 123; 688 static final int SINGLE_LISTBOX_CHOICE = 124; 689 static final int MESSAGE_RELAY = 125; 690 static final int SET_BACKGROUND_COLOR = 126; 691 static final int PLUGIN_STATE = 127; // plugin notifications 692 static final int SAVE_DOCUMENT_STATE = 128; 693 static final int GET_SELECTION = 129; 694 static final int WEBKIT_DRAW = 130; 695 static final int SYNC_SCROLL = 131; 696 static final int POST_URL = 132; 697 static final int SPLIT_PICTURE_SET = 133; 698 static final int CLEAR_CONTENT = 134; 699 700 // UI nav messages 701 static final int SET_MOVE_MOUSE = 135; 702 static final int SET_MOVE_MOUSE_IF_LATEST = 136; 703 static final int REQUEST_CURSOR_HREF = 137; 704 static final int ADD_JS_INTERFACE = 138; 705 static final int LOAD_DATA = 139; 706 707 // motion 708 static final int TOUCH_UP = 140; 709 // message used to pass UI touch events to WebCore 710 static final int TOUCH_EVENT = 141; 711 712 // Used to tell the focus controller not to draw the blinking cursor, 713 // based on whether the WebView has focus and whether the WebView's 714 // cursor matches the webpage's focus. 715 static final int SET_ACTIVE = 142; 716 717 // lifecycle activities for just this DOM (unlike pauseTimers, which 718 // is global) 719 static final int ON_PAUSE = 143; 720 static final int ON_RESUME = 144; 721 static final int FREE_MEMORY = 145; 722 723 // Network-based messaging 724 static final int CLEAR_SSL_PREF_TABLE = 150; 725 726 // Test harness messages 727 static final int REQUEST_EXT_REPRESENTATION = 160; 728 static final int REQUEST_DOC_AS_TEXT = 161; 729 730 // debugging 731 static final int DUMP_DOMTREE = 170; 732 static final int DUMP_RENDERTREE = 171; 733 static final int DUMP_NAVTREE = 172; 734 735 static final int SET_JS_FLAGS = 173; 736 737 // private message ids 738 private static final int DESTROY = 200; 739 740 // Private handler for WebCore messages. 741 private Handler mHandler; 742 // Message queue for containing messages before the WebCore thread is 743 // ready. 744 private ArrayList<Message> mMessages = new ArrayList<Message>(); 745 // Flag for blocking messages. This is used during DESTROY to avoid 746 // posting more messages to the EventHub or to WebView's event handler. 747 private boolean mBlockMessages; 748 749 private int mTid; 750 private int mSavedPriority; 751 752 /** 753 * Prevent other classes from creating an EventHub. 754 */ 755 private EventHub() {} 756 757 /** 758 * Transfer all messages to the newly created webcore thread handler. 759 */ 760 private void transferMessages() { 761 mTid = Process.myTid(); 762 mSavedPriority = Process.getThreadPriority(mTid); 763 764 mHandler = new Handler() { 765 @Override 766 public void handleMessage(Message msg) { 767 if (DebugFlags.WEB_VIEW_CORE) { 768 Log.v(LOGTAG, (msg.what < SCROLL_TEXT_INPUT || msg.what 769 > FREE_MEMORY ? Integer.toString(msg.what) 770 : HandlerDebugString[msg.what 771 - SCROLL_TEXT_INPUT]) 772 + " arg1=" + msg.arg1 + " arg2=" + msg.arg2 773 + " obj=" + msg.obj); 774 } 775 switch (msg.what) { 776 case WEBKIT_DRAW: 777 webkitDraw(); 778 break; 779 780 case DESTROY: 781 // Time to take down the world. Cancel all pending 782 // loads and destroy the native view and frame. 783 mBrowserFrame.destroy(); 784 mBrowserFrame = null; 785 mNativeClass = 0; 786 break; 787 788 case SCROLL_TEXT_INPUT: 789 nativeScrollFocusedTextInput(msg.arg1, msg.arg2); 790 break; 791 792 case LOAD_URL: 793 loadUrl((String) msg.obj); 794 break; 795 796 case POST_URL: { 797 PostUrlData param = (PostUrlData) msg.obj; 798 mBrowserFrame.postUrl(param.mUrl, param.mPostData); 799 break; 800 } 801 case LOAD_DATA: 802 BaseUrlData loadParams = (BaseUrlData) msg.obj; 803 String baseUrl = loadParams.mBaseUrl; 804 if (baseUrl != null) { 805 int i = baseUrl.indexOf(':'); 806 if (i > 0) { 807 /* 808 * In 1.0, {@link 809 * WebView#loadDataWithBaseURL} can access 810 * local asset files as long as the data is 811 * valid. In the new WebKit, the restriction 812 * is tightened. To be compatible with 1.0, 813 * we automatically add the scheme of the 814 * baseUrl for local access as long as it is 815 * not http(s)/ftp(s)/about/javascript 816 */ 817 String scheme = baseUrl.substring(0, i); 818 if (!scheme.startsWith("http") && 819 !scheme.startsWith("ftp") && 820 !scheme.startsWith("about") && 821 !scheme.startsWith("javascript")) { 822 nativeRegisterURLSchemeAsLocal(scheme); 823 } 824 } 825 } 826 mBrowserFrame.loadData(baseUrl, 827 loadParams.mData, 828 loadParams.mMimeType, 829 loadParams.mEncoding, 830 loadParams.mFailUrl); 831 break; 832 833 case STOP_LOADING: 834 // If the WebCore has committed the load, but not 835 // finished the first layout yet, we need to set 836 // first layout done to trigger the interpreted side sync 837 // up with native side 838 if (mBrowserFrame.committed() 839 && !mBrowserFrame.firstLayoutDone()) { 840 mBrowserFrame.didFirstLayout(); 841 } 842 // Do this after syncing up the layout state. 843 stopLoading(); 844 break; 845 846 case RELOAD: 847 mBrowserFrame.reload(false); 848 break; 849 850 case KEY_DOWN: 851 key((KeyEvent) msg.obj, true); 852 break; 853 854 case KEY_UP: 855 key((KeyEvent) msg.obj, false); 856 break; 857 858 case CLICK: 859 nativeClick(msg.arg1, msg.arg2); 860 break; 861 862 case VIEW_SIZE_CHANGED: 863 viewSizeChanged(msg.arg1, msg.arg2, 864 ((Float) msg.obj).floatValue()); 865 break; 866 867 case SET_SCROLL_OFFSET: 868 // note: these are in document coordinates 869 // (inv-zoom) 870 Point pt = (Point) msg.obj; 871 nativeSetScrollOffset(msg.arg1, pt.x, pt.y); 872 break; 873 874 case SET_GLOBAL_BOUNDS: 875 Rect r = (Rect) msg.obj; 876 nativeSetGlobalBounds(r.left, r.top, r.width(), 877 r.height()); 878 break; 879 880 case GO_BACK_FORWARD: 881 // If it is a standard load and the load is not 882 // committed yet, we interpret BACK as RELOAD 883 if (!mBrowserFrame.committed() && msg.arg1 == -1 && 884 (mBrowserFrame.loadType() == 885 BrowserFrame.FRAME_LOADTYPE_STANDARD)) { 886 mBrowserFrame.reload(true); 887 } else { 888 mBrowserFrame.goBackOrForward(msg.arg1); 889 } 890 break; 891 892 case RESTORE_STATE: 893 stopLoading(); 894 restoreState(msg.arg1); 895 break; 896 897 case PAUSE_TIMERS: 898 mSavedPriority = Process.getThreadPriority(mTid); 899 Process.setThreadPriority(mTid, 900 Process.THREAD_PRIORITY_BACKGROUND); 901 pauseTimers(); 902 if (CacheManager.disableTransaction()) { 903 WebCoreThread.mCacheTickersBlocked = true; 904 sWebCoreHandler.removeMessages( 905 WebCoreThread.CACHE_TICKER); 906 } 907 break; 908 909 case RESUME_TIMERS: 910 Process.setThreadPriority(mTid, mSavedPriority); 911 resumeTimers(); 912 if (CacheManager.enableTransaction()) { 913 WebCoreThread.mCacheTickersBlocked = false; 914 sWebCoreHandler.sendMessageDelayed( 915 sWebCoreHandler.obtainMessage( 916 WebCoreThread.CACHE_TICKER), 917 WebCoreThread.CACHE_TICKER_INTERVAL); 918 } 919 break; 920 921 case ON_PAUSE: 922 nativePause(); 923 break; 924 925 case ON_RESUME: 926 nativeResume(); 927 break; 928 929 case FREE_MEMORY: 930 clearCache(false); 931 nativeFreeMemory(); 932 break; 933 934 case PLUGIN_STATE: 935 PluginStateData psd = (PluginStateData) msg.obj; 936 nativeUpdatePluginState(psd.mFrame, psd.mNode, psd.mState); 937 break; 938 939 case SET_NETWORK_STATE: 940 if (BrowserFrame.sJavaBridge == null) { 941 throw new IllegalStateException("No WebView " + 942 "has been created in this process!"); 943 } 944 BrowserFrame.sJavaBridge 945 .setNetworkOnLine(msg.arg1 == 1); 946 break; 947 948 case CLEAR_CACHE: 949 clearCache(msg.arg1 == 1); 950 break; 951 952 case CLEAR_HISTORY: 953 mCallbackProxy.getBackForwardList(). 954 close(mBrowserFrame.mNativeFrame); 955 break; 956 957 case REPLACE_TEXT: 958 ReplaceTextData rep = (ReplaceTextData) msg.obj; 959 nativeReplaceTextfieldText(msg.arg1, msg.arg2, 960 rep.mReplace, rep.mNewStart, rep.mNewEnd, 961 rep.mTextGeneration); 962 break; 963 964 case PASS_TO_JS: { 965 JSKeyData jsData = (JSKeyData) msg.obj; 966 KeyEvent evt = jsData.mEvent; 967 int keyCode = evt.getKeyCode(); 968 int keyValue = evt.getUnicodeChar(); 969 int generation = msg.arg1; 970 passToJs(generation, 971 jsData.mCurrentText, 972 keyCode, 973 keyValue, 974 evt.isDown(), 975 evt.isShiftPressed(), evt.isAltPressed(), 976 evt.isSymPressed()); 977 break; 978 } 979 980 case SAVE_DOCUMENT_STATE: { 981 CursorData cDat = (CursorData) msg.obj; 982 nativeSaveDocumentState(cDat.mFrame); 983 break; 984 } 985 986 case CLEAR_SSL_PREF_TABLE: 987 Network.getInstance(mContext) 988 .clearUserSslPrefTable(); 989 break; 990 991 case TOUCH_UP: 992 TouchUpData touchUpData = (TouchUpData) msg.obj; 993 nativeTouchUp(touchUpData.mMoveGeneration, 994 touchUpData.mFrame, touchUpData.mNode, 995 touchUpData.mX, touchUpData.mY, 996 touchUpData.mSize); 997 break; 998 999 case TOUCH_EVENT: { 1000 TouchEventData ted = (TouchEventData) msg.obj; 1001 Message.obtain( 1002 mWebView.mPrivateHandler, 1003 WebView.PREVENT_TOUCH_ID, ted.mAction, 1004 nativeHandleTouchEvent(ted.mAction, ted.mX, 1005 ted.mY) ? 1 : 0).sendToTarget(); 1006 break; 1007 } 1008 1009 case SET_ACTIVE: 1010 nativeSetFocusControllerActive(msg.arg1 == 1); 1011 break; 1012 1013 case ADD_JS_INTERFACE: 1014 JSInterfaceData jsData = (JSInterfaceData) msg.obj; 1015 mBrowserFrame.addJavascriptInterface(jsData.mObject, 1016 jsData.mInterfaceName); 1017 break; 1018 1019 case REQUEST_EXT_REPRESENTATION: 1020 mBrowserFrame.externalRepresentation( 1021 (Message) msg.obj); 1022 break; 1023 1024 case REQUEST_DOC_AS_TEXT: 1025 mBrowserFrame.documentAsText((Message) msg.obj); 1026 break; 1027 1028 case SET_MOVE_MOUSE: 1029 CursorData cursorData = (CursorData) msg.obj; 1030 nativeMoveMouse(cursorData.mFrame, 1031 cursorData.mX, cursorData.mY); 1032 break; 1033 1034 case SET_MOVE_MOUSE_IF_LATEST: 1035 CursorData cData = (CursorData) msg.obj; 1036 nativeMoveMouseIfLatest(cData.mMoveGeneration, 1037 cData.mFrame, 1038 cData.mX, cData.mY); 1039 break; 1040 1041 case REQUEST_CURSOR_HREF: { 1042 Message hrefMsg = (Message) msg.obj; 1043 String res = nativeRetrieveHref(msg.arg1, msg.arg2); 1044 hrefMsg.getData().putString("url", res); 1045 hrefMsg.sendToTarget(); 1046 break; 1047 } 1048 1049 case UPDATE_CACHE_AND_TEXT_ENTRY: 1050 nativeUpdateFrameCache(); 1051 // FIXME: this should provide a minimal rectangle 1052 if (mWebView != null) { 1053 mWebView.postInvalidate(); 1054 } 1055 sendUpdateTextEntry(); 1056 break; 1057 1058 case DOC_HAS_IMAGES: 1059 Message imageResult = (Message) msg.obj; 1060 imageResult.arg1 = 1061 mBrowserFrame.documentHasImages() ? 1 : 0; 1062 imageResult.sendToTarget(); 1063 break; 1064 1065 case SET_SNAP_ANCHOR: 1066 nativeSetSnapAnchor(msg.arg1, msg.arg2); 1067 break; 1068 1069 case DELETE_SELECTION: 1070 DeleteSelectionData deleteSelectionData 1071 = (DeleteSelectionData) msg.obj; 1072 nativeDeleteSelection(deleteSelectionData.mStart, 1073 deleteSelectionData.mEnd, 1074 deleteSelectionData.mTextGeneration); 1075 break; 1076 1077 case SET_SELECTION: 1078 nativeSetSelection(msg.arg1, msg.arg2); 1079 break; 1080 1081 case LISTBOX_CHOICES: 1082 SparseBooleanArray choices = (SparseBooleanArray) 1083 msg.obj; 1084 int choicesSize = msg.arg1; 1085 boolean[] choicesArray = new boolean[choicesSize]; 1086 for (int c = 0; c < choicesSize; c++) { 1087 choicesArray[c] = choices.get(c); 1088 } 1089 nativeSendListBoxChoices(choicesArray, 1090 choicesSize); 1091 break; 1092 1093 case SINGLE_LISTBOX_CHOICE: 1094 nativeSendListBoxChoice(msg.arg1); 1095 break; 1096 1097 case SET_BACKGROUND_COLOR: 1098 nativeSetBackgroundColor(msg.arg1); 1099 break; 1100 1101 case GET_SELECTION: 1102 String str = nativeGetSelection((Region) msg.obj); 1103 Message.obtain(mWebView.mPrivateHandler 1104 , WebView.UPDATE_CLIPBOARD, str) 1105 .sendToTarget(); 1106 break; 1107 1108 case DUMP_DOMTREE: 1109 nativeDumpDomTree(msg.arg1 == 1); 1110 break; 1111 1112 case DUMP_RENDERTREE: 1113 nativeDumpRenderTree(msg.arg1 == 1); 1114 break; 1115 1116 case DUMP_NAVTREE: 1117 nativeDumpNavTree(); 1118 break; 1119 1120 case SET_JS_FLAGS: 1121 nativeSetJsFlags((String)msg.obj); 1122 break; 1123 1124 case SYNC_SCROLL: 1125 mWebkitScrollX = msg.arg1; 1126 mWebkitScrollY = msg.arg2; 1127 break; 1128 1129 case SPLIT_PICTURE_SET: 1130 nativeSplitContent(); 1131 mSplitPictureIsScheduled = false; 1132 break; 1133 1134 case CLEAR_CONTENT: 1135 // Clear the view so that onDraw() will draw nothing 1136 // but white background 1137 // (See public method WebView.clearView) 1138 nativeClearContent(); 1139 break; 1140 1141 case MESSAGE_RELAY: 1142 if (msg.obj instanceof Message) { 1143 ((Message) msg.obj).sendToTarget(); 1144 } 1145 break; 1146 } 1147 } 1148 }; 1149 // Take all queued messages and resend them to the new handler. 1150 synchronized (this) { 1151 int size = mMessages.size(); 1152 for (int i = 0; i < size; i++) { 1153 mHandler.sendMessage(mMessages.get(i)); 1154 } 1155 mMessages = null; 1156 } 1157 } 1158 1159 /** 1160 * Send a message internally to the queue or to the handler 1161 */ 1162 private synchronized void sendMessage(Message msg) { 1163 if (mBlockMessages) { 1164 return; 1165 } 1166 if (mMessages != null) { 1167 mMessages.add(msg); 1168 } else { 1169 mHandler.sendMessage(msg); 1170 } 1171 } 1172 1173 private synchronized void removeMessages(int what) { 1174 if (mBlockMessages) { 1175 return; 1176 } 1177 if (what == EventHub.WEBKIT_DRAW) { 1178 mDrawIsScheduled = false; 1179 } 1180 if (mMessages != null) { 1181 Log.w(LOGTAG, "Not supported in this case."); 1182 } else { 1183 mHandler.removeMessages(what); 1184 } 1185 } 1186 1187 private synchronized void sendMessageDelayed(Message msg, long delay) { 1188 if (mBlockMessages) { 1189 return; 1190 } 1191 mHandler.sendMessageDelayed(msg, delay); 1192 } 1193 1194 /** 1195 * Send a message internally to the front of the queue. 1196 */ 1197 private synchronized void sendMessageAtFrontOfQueue(Message msg) { 1198 if (mBlockMessages) { 1199 return; 1200 } 1201 if (mMessages != null) { 1202 mMessages.add(0, msg); 1203 } else { 1204 mHandler.sendMessageAtFrontOfQueue(msg); 1205 } 1206 } 1207 1208 /** 1209 * Remove all the messages. 1210 */ 1211 private synchronized void removeMessages() { 1212 // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed 1213 mDrawIsScheduled = false; 1214 mSplitPictureIsScheduled = false; 1215 if (mMessages != null) { 1216 mMessages.clear(); 1217 } else { 1218 mHandler.removeCallbacksAndMessages(null); 1219 } 1220 } 1221 1222 /** 1223 * Block sending messages to the EventHub. 1224 */ 1225 private synchronized void blockMessages() { 1226 mBlockMessages = true; 1227 } 1228 } 1229 1230 //------------------------------------------------------------------------- 1231 // Methods called by host activity (in the same thread) 1232 //------------------------------------------------------------------------- 1233 1234 void stopLoading() { 1235 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading"); 1236 if (mBrowserFrame != null) { 1237 mBrowserFrame.stopLoading(); 1238 } 1239 } 1240 1241 //------------------------------------------------------------------------- 1242 // Methods called by WebView 1243 // If it refers to local variable, it needs synchronized(). 1244 // If it needs WebCore, it has to send message. 1245 //------------------------------------------------------------------------- 1246 1247 void sendMessage(Message msg) { 1248 mEventHub.sendMessage(msg); 1249 } 1250 1251 void sendMessage(int what) { 1252 mEventHub.sendMessage(Message.obtain(null, what)); 1253 } 1254 1255 void sendMessage(int what, Object obj) { 1256 mEventHub.sendMessage(Message.obtain(null, what, obj)); 1257 } 1258 1259 void sendMessage(int what, int arg1) { 1260 // just ignore the second argument (make it 0) 1261 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0)); 1262 } 1263 1264 void sendMessage(int what, int arg1, int arg2) { 1265 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2)); 1266 } 1267 1268 void sendMessage(int what, int arg1, Object obj) { 1269 // just ignore the second argument (make it 0) 1270 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj)); 1271 } 1272 1273 void sendMessage(int what, int arg1, int arg2, Object obj) { 1274 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj)); 1275 } 1276 1277 void sendMessageDelayed(int what, Object obj, long delay) { 1278 mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay); 1279 } 1280 1281 void removeMessages(int what) { 1282 mEventHub.removeMessages(what); 1283 } 1284 1285 void removeMessages() { 1286 mEventHub.removeMessages(); 1287 } 1288 1289 /** 1290 * Removes pending messages and trigger a DESTROY message to send to 1291 * WebCore. 1292 * Called from UI thread. 1293 */ 1294 void destroy() { 1295 // We don't want anyone to post a message between removing pending 1296 // messages and sending the destroy message. 1297 synchronized (mEventHub) { 1298 mEventHub.removeMessages(); 1299 mEventHub.sendMessageAtFrontOfQueue( 1300 Message.obtain(null, EventHub.DESTROY)); 1301 mEventHub.blockMessages(); 1302 mWebView = null; 1303 } 1304 } 1305 1306 //------------------------------------------------------------------------- 1307 // WebViewCore private methods 1308 //------------------------------------------------------------------------- 1309 1310 private void clearCache(boolean includeDiskFiles) { 1311 mBrowserFrame.clearCache(); 1312 if (includeDiskFiles) { 1313 CacheManager.removeAllCacheFiles(); 1314 } 1315 } 1316 1317 private void loadUrl(String url) { 1318 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url); 1319 mBrowserFrame.loadUrl(url); 1320 } 1321 1322 private void key(KeyEvent evt, boolean isDown) { 1323 if (DebugFlags.WEB_VIEW_CORE) { 1324 Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", " 1325 + evt); 1326 } 1327 int keyCode = evt.getKeyCode(); 1328 if (!nativeKey(keyCode, evt.getUnicodeChar(), 1329 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(), 1330 evt.isSymPressed(), 1331 isDown) && keyCode != KeyEvent.KEYCODE_ENTER) { 1332 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP 1333 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { 1334 if (DebugFlags.WEB_VIEW_CORE) { 1335 Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode); 1336 } 1337 if (mWebView != null && evt.isDown()) { 1338 Message.obtain(mWebView.mPrivateHandler, 1339 WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget(); 1340 } 1341 return; 1342 } 1343 // bubble up the event handling 1344 // but do not bubble up the ENTER key, which would open the search 1345 // bar without any text. 1346 mCallbackProxy.onUnhandledKeyEvent(evt); 1347 } 1348 } 1349 1350 // These values are used to avoid requesting a layout based on old values 1351 private int mCurrentViewWidth = 0; 1352 private int mCurrentViewHeight = 0; 1353 1354 // notify webkit that our virtual view size changed size (after inv-zoom) 1355 private void viewSizeChanged(int w, int h, float scale) { 1356 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE onSizeChanged"); 1357 if (w == 0) { 1358 Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); 1359 return; 1360 } 1361 if (mSettings.getUseWideViewPort()) { 1362 int width; 1363 if (mViewportWidth == -1) { 1364 if (mSettings.getLayoutAlgorithm() == 1365 WebSettings.LayoutAlgorithm.NORMAL) { 1366 width = WebView.ZOOM_OUT_WIDTH; 1367 } else { 1368 /* 1369 * if a page's minimum preferred width is wider than the 1370 * given "w", use it instead to get better layout result. If 1371 * we start a page with MAX_ZOOM_WIDTH, "w" will be always 1372 * wider. If we start a page with screen width, due to the 1373 * delay between {@link #didFirstLayout} and 1374 * {@link #viewSizeChanged}, 1375 * {@link #nativeGetContentMinPrefWidth} will return a more 1376 * accurate value than initial 0 to result a better layout. 1377 * In the worse case, the native width will be adjusted when 1378 * next zoom or screen orientation change happens. 1379 */ 1380 width = Math.max(w, nativeGetContentMinPrefWidth()); 1381 } 1382 } else { 1383 width = Math.max(w, mViewportWidth); 1384 } 1385 // while in zoom overview mode, the text are wrapped to the screen 1386 // width matching mWebView.mLastScale. So that we don't trigger 1387 // re-flow while toggling between overview mode and normal mode. 1388 nativeSetSize(width, Math.round((float) width * h / w), 1389 Math.round(mWebView.mInZoomOverview ? w * scale 1390 / mWebView.mLastScale : w), scale, w, h); 1391 } else { 1392 nativeSetSize(w, h, w, scale, w, h); 1393 } 1394 // Remember the current width and height 1395 boolean needInvalidate = (mCurrentViewWidth == 0); 1396 mCurrentViewWidth = w; 1397 mCurrentViewHeight = h; 1398 if (needInvalidate) { 1399 // ensure {@link #webkitDraw} is called as we were blocking in 1400 // {@link #contentDraw} when mCurrentViewWidth is 0 1401 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged"); 1402 contentDraw(); 1403 } 1404 mEventHub.sendMessage(Message.obtain(null, 1405 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY)); 1406 } 1407 1408 private void sendUpdateTextEntry() { 1409 if (mWebView != null) { 1410 Message.obtain(mWebView.mPrivateHandler, 1411 WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget(); 1412 } 1413 } 1414 1415 // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize 1416 // callbacks. Computes the sum of database quota for all origins. 1417 private long getUsedQuota() { 1418 WebStorage webStorage = WebStorage.getInstance(); 1419 Set<String> origins = webStorage.getOrigins(); 1420 if (origins == null) { 1421 return 0; 1422 } 1423 long usedQuota = 0; 1424 for (String origin : origins) { 1425 usedQuota += webStorage.getQuotaForOrigin(origin); 1426 } 1427 return usedQuota; 1428 } 1429 1430 // Used to avoid posting more than one draw message. 1431 private boolean mDrawIsScheduled; 1432 1433 // Used to avoid posting more than one split picture message. 1434 private boolean mSplitPictureIsScheduled; 1435 1436 // Used to suspend drawing. 1437 private boolean mDrawIsPaused; 1438 1439 // Used to end scale+scroll mode, accessed by both threads 1440 boolean mEndScaleZoom = false; 1441 1442 public class DrawData { 1443 public DrawData() { 1444 mInvalRegion = new Region(); 1445 mWidthHeight = new Point(); 1446 } 1447 public Region mInvalRegion; 1448 public Point mViewPoint; 1449 public Point mWidthHeight; 1450 public int mMinPrefWidth; 1451 } 1452 1453 private void webkitDraw() { 1454 mDrawIsScheduled = false; 1455 DrawData draw = new DrawData(); 1456 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); 1457 if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight) 1458 == false) { 1459 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort"); 1460 return; 1461 } 1462 if (mWebView != null) { 1463 // Send the native view size that was used during the most recent 1464 // layout. 1465 draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight); 1466 if (WebView.ENABLE_DOUBLETAP_ZOOM && mSettings.getUseWideViewPort()) { 1467 draw.mMinPrefWidth = nativeGetContentMinPrefWidth(); 1468 } 1469 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); 1470 Message.obtain(mWebView.mPrivateHandler, 1471 WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); 1472 if (mWebkitScrollX != 0 || mWebkitScrollY != 0) { 1473 // as we have the new picture, try to sync the scroll position 1474 Message.obtain(mWebView.mPrivateHandler, 1475 WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX, 1476 mWebkitScrollY).sendToTarget(); 1477 mWebkitScrollX = mWebkitScrollY = 0; 1478 } 1479 // nativeSnapToAnchor() needs to be called after NEW_PICTURE_MSG_ID 1480 // is sent, so that scroll will be based on the new content size. 1481 nativeSnapToAnchor(); 1482 } 1483 } 1484 1485 /////////////////////////////////////////////////////////////////////////// 1486 // These are called from the UI thread, not our thread 1487 1488 static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG | 1489 Paint.DITHER_FLAG | 1490 Paint.SUBPIXEL_TEXT_FLAG; 1491 static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG | 1492 Paint.DITHER_FLAG; 1493 1494 final DrawFilter mZoomFilter = 1495 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG); 1496 final DrawFilter mScrollFilter = 1497 new PaintFlagsDrawFilter(SCROLL_BITS, 0); 1498 1499 /* package */ void drawContentPicture(Canvas canvas, int color, 1500 boolean animatingZoom, 1501 boolean animatingScroll) { 1502 DrawFilter df = null; 1503 if (animatingZoom) { 1504 df = mZoomFilter; 1505 } else if (animatingScroll) { 1506 df = mScrollFilter; 1507 } 1508 canvas.setDrawFilter(df); 1509 boolean tookTooLong = nativeDrawContent(canvas, color); 1510 canvas.setDrawFilter(null); 1511 if (tookTooLong && mSplitPictureIsScheduled == false) { 1512 mSplitPictureIsScheduled = true; 1513 sendMessage(EventHub.SPLIT_PICTURE_SET); 1514 } 1515 } 1516 1517 /* package */ boolean pictureReady() { 1518 return nativePictureReady(); 1519 } 1520 1521 /*package*/ Picture copyContentPicture() { 1522 Picture result = new Picture(); 1523 nativeCopyContentToPicture(result); 1524 return result; 1525 } 1526 1527 static void pauseUpdate(WebViewCore core) { 1528 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1529 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1530 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1531 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1532 .obtainMessage(WebCoreThread.REDUCE_PRIORITY)); 1533 // Note: there is one possible failure mode. If pauseUpdate() is called 1534 // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out 1535 // of the queue and about to be executed. mDrawIsScheduled may be set to 1536 // false in webkitDraw(). So update won't be blocked. But at least the 1537 // webcore thread priority is still lowered. 1538 if (core != null) { 1539 synchronized (core) { 1540 core.mDrawIsPaused = true; 1541 core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW); 1542 } 1543 } 1544 } 1545 1546 static void resumeUpdate(WebViewCore core) { 1547 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1548 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1549 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1550 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1551 .obtainMessage(WebCoreThread.RESUME_PRIORITY)); 1552 if (core != null) { 1553 synchronized (core) { 1554 core.mDrawIsScheduled = false; 1555 core.mDrawIsPaused = false; 1556 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate"); 1557 core.contentDraw(); 1558 } 1559 } 1560 } 1561 1562 static void startCacheTransaction() { 1563 sWebCoreHandler.sendMessage(sWebCoreHandler 1564 .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER)); 1565 } 1566 1567 static void endCacheTransaction() { 1568 sWebCoreHandler.sendMessage(sWebCoreHandler 1569 .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER)); 1570 } 1571 1572 ////////////////////////////////////////////////////////////////////////// 1573 1574 private void restoreState(int index) { 1575 WebBackForwardList list = mCallbackProxy.getBackForwardList(); 1576 int size = list.getSize(); 1577 for (int i = 0; i < size; i++) { 1578 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); 1579 } 1580 mBrowserFrame.mLoadInitFromJava = true; 1581 list.restoreIndex(mBrowserFrame.mNativeFrame, index); 1582 mBrowserFrame.mLoadInitFromJava = false; 1583 } 1584 1585 //------------------------------------------------------------------------- 1586 // Implement abstract methods in WebViewCore, native WebKit callback part 1587 //------------------------------------------------------------------------- 1588 1589 // called from JNI or WebView thread 1590 /* package */ void contentDraw() { 1591 // don't update the Picture until we have an initial width and finish 1592 // the first layout 1593 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { 1594 return; 1595 } 1596 // only fire an event if this is our first request 1597 synchronized (this) { 1598 if (mDrawIsPaused || mDrawIsScheduled) { 1599 return; 1600 } 1601 mDrawIsScheduled = true; 1602 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 1603 } 1604 } 1605 1606 // called by JNI 1607 private void contentScrollBy(int dx, int dy, boolean animate) { 1608 if (!mBrowserFrame.firstLayoutDone()) { 1609 // Will this happen? If yes, we need to do something here. 1610 return; 1611 } 1612 if (mWebView != null) { 1613 Message msg = Message.obtain(mWebView.mPrivateHandler, 1614 WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate)); 1615 if (mDrawIsScheduled) { 1616 mEventHub.sendMessage(Message.obtain(null, 1617 EventHub.MESSAGE_RELAY, msg)); 1618 } else { 1619 msg.sendToTarget(); 1620 } 1621 } 1622 } 1623 1624 // called by JNI 1625 private void contentScrollTo(int x, int y) { 1626 if (!mBrowserFrame.firstLayoutDone()) { 1627 /* 1628 * WebKit restore state will be called before didFirstLayout(), 1629 * remember the position as it has to be applied after restoring 1630 * zoom factor which is controlled by screenWidth. 1631 */ 1632 mRestoredX = x; 1633 mRestoredY = y; 1634 return; 1635 } 1636 if (mWebView != null) { 1637 Message msg = Message.obtain(mWebView.mPrivateHandler, 1638 WebView.SCROLL_TO_MSG_ID, x, y); 1639 if (mDrawIsScheduled) { 1640 mEventHub.sendMessage(Message.obtain(null, 1641 EventHub.MESSAGE_RELAY, msg)); 1642 } else { 1643 msg.sendToTarget(); 1644 } 1645 } 1646 } 1647 1648 // called by JNI 1649 private void contentSpawnScrollTo(int x, int y) { 1650 if (!mBrowserFrame.firstLayoutDone()) { 1651 /* 1652 * WebKit restore state will be called before didFirstLayout(), 1653 * remember the position as it has to be applied after restoring 1654 * zoom factor which is controlled by screenWidth. 1655 */ 1656 mRestoredX = x; 1657 mRestoredY = y; 1658 return; 1659 } 1660 if (mWebView != null) { 1661 Message msg = Message.obtain(mWebView.mPrivateHandler, 1662 WebView.SPAWN_SCROLL_TO_MSG_ID, x, y); 1663 if (mDrawIsScheduled) { 1664 mEventHub.sendMessage(Message.obtain(null, 1665 EventHub.MESSAGE_RELAY, msg)); 1666 } else { 1667 msg.sendToTarget(); 1668 } 1669 } 1670 } 1671 1672 // called by JNI 1673 private void sendNotifyProgressFinished() { 1674 sendUpdateTextEntry(); 1675 // as CacheManager can behave based on database transaction, we need to 1676 // call tick() to trigger endTransaction 1677 sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER); 1678 sWebCoreHandler.sendMessage(sWebCoreHandler 1679 .obtainMessage(WebCoreThread.CACHE_TICKER)); 1680 contentDraw(); 1681 } 1682 1683 /* Called by JNI. The coordinates are in doc coordinates, so they need to 1684 be scaled before they can be used by the view system, which happens 1685 in WebView since it (and its thread) know the current scale factor. 1686 */ 1687 private void sendViewInvalidate(int left, int top, int right, int bottom) { 1688 if (mWebView != null) { 1689 Message.obtain(mWebView.mPrivateHandler, 1690 WebView.INVAL_RECT_MSG_ID, 1691 new Rect(left, top, right, bottom)).sendToTarget(); 1692 } 1693 } 1694 1695 /* package */ WebView getWebView() { 1696 return mWebView; 1697 } 1698 1699 private native void setViewportSettingsFromNative(); 1700 1701 // called by JNI 1702 private void didFirstLayout(boolean standardLoad) { 1703 // Trick to ensure that the Picture has the exact height for the content 1704 // by forcing to layout with 0 height after the page is ready, which is 1705 // indicated by didFirstLayout. This is essential to get rid of the 1706 // white space in the GMail which uses WebView for message view. 1707 if (mWebView != null && mWebView.mHeightCanMeasure) { 1708 mWebView.mLastHeightSent = 0; 1709 // Send a negative scale to indicate that WebCore should reuse the 1710 // current scale 1711 mEventHub.sendMessage(Message.obtain(null, 1712 EventHub.VIEW_SIZE_CHANGED, mWebView.mLastWidthSent, 1713 mWebView.mLastHeightSent, -1.0f)); 1714 } 1715 1716 mBrowserFrame.didFirstLayout(); 1717 1718 // reset the scroll position as it is a new page now 1719 mWebkitScrollX = mWebkitScrollY = 0; 1720 1721 // set the viewport settings from WebKit 1722 setViewportSettingsFromNative(); 1723 1724 // adjust the default scale to match the density 1725 if (WebView.DEFAULT_SCALE_PERCENT != 100) { 1726 float adjust = (float) WebView.DEFAULT_SCALE_PERCENT / 100.0f; 1727 if (mViewportInitialScale > 0) { 1728 mViewportInitialScale *= adjust; 1729 } 1730 if (mViewportMinimumScale > 0) { 1731 mViewportMinimumScale *= adjust; 1732 } 1733 if (mViewportMaximumScale > 0) { 1734 mViewportMaximumScale *= adjust; 1735 } 1736 } 1737 1738 // infer the values if they are not defined. 1739 if (mViewportWidth == 0) { 1740 if (mViewportInitialScale == 0) { 1741 mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT; 1742 } 1743 if (mViewportMinimumScale == 0) { 1744 mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT; 1745 } 1746 } 1747 if (mViewportUserScalable == false) { 1748 mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT; 1749 mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT; 1750 mViewportMaximumScale = WebView.DEFAULT_SCALE_PERCENT; 1751 } 1752 if (mViewportMinimumScale > mViewportInitialScale) { 1753 if (mViewportInitialScale == 0) { 1754 mViewportInitialScale = mViewportMinimumScale; 1755 } else { 1756 mViewportMinimumScale = mViewportInitialScale; 1757 } 1758 } 1759 if (mViewportMaximumScale > 0) { 1760 if (mViewportMaximumScale < mViewportInitialScale) { 1761 mViewportMaximumScale = mViewportInitialScale; 1762 } else if (mViewportInitialScale == 0) { 1763 mViewportInitialScale = mViewportMaximumScale; 1764 } 1765 } 1766 if (mViewportWidth < 0 1767 && mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) { 1768 mViewportWidth = 0; 1769 } 1770 1771 // now notify webview 1772 if (mWebView != null) { 1773 WebView.ScaleLimitData scaleLimit = new WebView.ScaleLimitData(); 1774 scaleLimit.mMinScale = mViewportMinimumScale; 1775 scaleLimit.mMaxScale = mViewportMaximumScale; 1776 1777 if (mRestoredScale > 0) { 1778 Message.obtain(mWebView.mPrivateHandler, 1779 WebView.DID_FIRST_LAYOUT_MSG_ID, 1780 mRestoredScreenWidthScale > 0 ? 1781 -mRestoredScreenWidthScale : mRestoredScale, 0, 1782 scaleLimit).sendToTarget(); 1783 } else { 1784 // if standardLoad is true, use mViewportInitialScale, otherwise 1785 // pass -1 to the WebView to indicate no change of the scale. 1786 Message.obtain(mWebView.mPrivateHandler, 1787 WebView.DID_FIRST_LAYOUT_MSG_ID, 1788 standardLoad ? mViewportInitialScale : -1, 1789 mViewportWidth, scaleLimit).sendToTarget(); 1790 } 1791 1792 // force an early draw for quick feedback after the first layout 1793 if (mCurrentViewWidth != 0) { 1794 synchronized (this) { 1795 if (mDrawIsScheduled) { 1796 mEventHub.removeMessages(EventHub.WEBKIT_DRAW); 1797 } 1798 mDrawIsScheduled = true; 1799 // if no restored offset, move the new page to (0, 0) 1800 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 1801 EventHub.MESSAGE_RELAY, Message.obtain( 1802 mWebView.mPrivateHandler, 1803 WebView.SCROLL_TO_MSG_ID, mRestoredX, 1804 mRestoredY))); 1805 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 1806 EventHub.WEBKIT_DRAW)); 1807 } 1808 } 1809 1810 // reset restored offset, scale 1811 mRestoredX = mRestoredY = mRestoredScale = mRestoredScreenWidthScale = 0; 1812 } 1813 } 1814 1815 // called by JNI 1816 private void restoreScale(int scale) { 1817 if (mBrowserFrame.firstLayoutDone() == false) { 1818 mRestoredScale = scale; 1819 } 1820 } 1821 1822 // called by JNI 1823 private void restoreScreenWidthScale(int scale) { 1824 if (!WebView.ENABLE_DOUBLETAP_ZOOM || !mSettings.getUseWideViewPort()) { 1825 return; 1826 } 1827 1828 if (mBrowserFrame.firstLayoutDone() == false) { 1829 mRestoredScreenWidthScale = scale; 1830 } 1831 } 1832 1833 // called by JNI 1834 private void needTouchEvents(boolean need) { 1835 if (mWebView != null) { 1836 Message.obtain(mWebView.mPrivateHandler, 1837 WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0) 1838 .sendToTarget(); 1839 } 1840 } 1841 1842 // called by JNI 1843 private void updateTextfield(int ptr, boolean changeToPassword, 1844 String text, int textGeneration) { 1845 if (mWebView != null) { 1846 Message msg = Message.obtain(mWebView.mPrivateHandler, 1847 WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 1848 textGeneration, text); 1849 msg.getData().putBoolean("password", changeToPassword); 1850 msg.sendToTarget(); 1851 } 1852 } 1853 1854 // called by JNI 1855 private void clearTextEntry() { 1856 if (mWebView == null) return; 1857 Message.obtain(mWebView.mPrivateHandler, 1858 WebView.CLEAR_TEXT_ENTRY).sendToTarget(); 1859 } 1860 1861 /** 1862 * Scroll the focused textfield to (x, y) in document space 1863 */ 1864 private native void nativeScrollFocusedTextInput(int x, int y); 1865 1866 // these must be in document space (i.e. not scaled/zoomed). 1867 private native void nativeSetScrollOffset(int gen, int dx, int dy); 1868 1869 private native void nativeSetGlobalBounds(int x, int y, int w, int h); 1870 1871 // called by JNI 1872 private void requestListBox(String[] array, boolean[] enabledArray, 1873 int[] selectedArray) { 1874 if (mWebView != null) { 1875 mWebView.requestListBox(array, enabledArray, selectedArray); 1876 } 1877 } 1878 1879 // called by JNI 1880 private void requestListBox(String[] array, boolean[] enabledArray, 1881 int selection) { 1882 if (mWebView != null) { 1883 mWebView.requestListBox(array, enabledArray, selection); 1884 } 1885 1886 } 1887 1888 // called by JNI 1889 private void requestKeyboard(boolean showKeyboard) { 1890 if (mWebView != null) { 1891 Message.obtain(mWebView.mPrivateHandler, 1892 WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0) 1893 .sendToTarget(); 1894 } 1895 } 1896 1897 // This class looks like a SurfaceView to native code. In java, we can 1898 // assume the passed in SurfaceView is this class so we can talk to the 1899 // ViewManager through the ChildView. 1900 private class SurfaceViewProxy extends SurfaceView 1901 implements SurfaceHolder.Callback { 1902 private final ViewManager.ChildView mChildView; 1903 private int mPointer; 1904 SurfaceViewProxy(Context context, ViewManager.ChildView childView, 1905 int pointer) { 1906 super(context); 1907 setWillNotDraw(false); // this prevents the black box artifact 1908 getHolder().addCallback(this); 1909 mChildView = childView; 1910 mChildView.mView = this; 1911 mPointer = pointer; 1912 } 1913 void destroy() { 1914 mPointer = 0; 1915 mChildView.removeView(); 1916 } 1917 void attach(int x, int y, int width, int height) { 1918 mChildView.attachView(x, y, width, height); 1919 } 1920 1921 // SurfaceHolder.Callback methods 1922 public void surfaceCreated(SurfaceHolder holder) { 1923 if (mPointer != 0) { 1924 nativeSurfaceChanged(mPointer, 0, 0, 0, 0); 1925 } 1926 } 1927 public void surfaceChanged(SurfaceHolder holder, int format, int width, 1928 int height) { 1929 if (mPointer != 0) { 1930 nativeSurfaceChanged(mPointer, 1, format, width, height); 1931 } 1932 } 1933 public void surfaceDestroyed(SurfaceHolder holder) { 1934 if (mPointer != 0) { 1935 nativeSurfaceChanged(mPointer, 2, 0, 0, 0); 1936 } 1937 } 1938 } 1939 1940 // PluginWidget functions for mainting SurfaceViews for the Surface drawing 1941 // model. 1942 private SurfaceView createSurface(int nativePointer) { 1943 if (mWebView == null) { 1944 return null; 1945 } 1946 return new SurfaceViewProxy(mContext, 1947 mWebView.mViewManager.createView(), nativePointer); 1948 } 1949 1950 private void destroySurface(SurfaceView surface) { 1951 SurfaceViewProxy proxy = (SurfaceViewProxy) surface; 1952 proxy.destroy(); 1953 } 1954 1955 private void attachSurface(SurfaceView surface, int x, int y, 1956 int width, int height) { 1957 SurfaceViewProxy proxy = (SurfaceViewProxy) surface; 1958 proxy.attach(x, y, width, height); 1959 } 1960 1961 // Callback for the SurfaceHolder.Callback. Called for all the surface 1962 // callbacks. The state parameter is one of Created(0), Changed(1), 1963 // Destroyed(2). 1964 private native void nativeSurfaceChanged(int pointer, int state, int format, 1965 int width, int height); 1966 1967 private native void nativePause(); 1968 private native void nativeResume(); 1969 private native void nativeFreeMemory(); 1970} 1971