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