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