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