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