WebViewCore.java revision 82aa2f5eecddf723af2e4e134de15bbf5c6b32f4
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 if (!nativeKey(evt.getKeyCode(), evt.getUnicodeChar(), 1234 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(), 1235 isDown)) { 1236 // bubble up the event handling 1237 mCallbackProxy.onUnhandledKeyEvent(evt); 1238 } 1239 } 1240 1241 // These values are used to avoid requesting a layout based on old values 1242 private int mCurrentViewWidth = 0; 1243 private int mCurrentViewHeight = 0; 1244 1245 // notify webkit that our virtual view size changed size (after inv-zoom) 1246 private void viewSizeChanged(int w, int h, float scale) { 1247 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE onSizeChanged"); 1248 if (w == 0) { 1249 Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); 1250 return; 1251 } 1252 if (mSettings.getUseWideViewPort() 1253 && (w < mViewportWidth || mViewportWidth == -1)) { 1254 int width = mViewportWidth; 1255 if (mViewportWidth == -1) { 1256 if (mSettings.getLayoutAlgorithm() == 1257 WebSettings.LayoutAlgorithm.NORMAL) { 1258 width = WebView.ZOOM_OUT_WIDTH; 1259 } else { 1260 /* 1261 * if a page's minimum preferred width is wider than the 1262 * given "w", use it instead to get better layout result. If 1263 * we start a page with MAX_ZOOM_WIDTH, "w" will be always 1264 * wider. If we start a page with screen width, due to the 1265 * delay between {@link #didFirstLayout} and 1266 * {@link #viewSizeChanged}, 1267 * {@link #nativeGetContentMinPrefWidth} will return a more 1268 * accurate value than initial 0 to result a better layout. 1269 * In the worse case, the native width will be adjusted when 1270 * next zoom or screen orientation change happens. 1271 */ 1272 width = Math.max(w, nativeGetContentMinPrefWidth()); 1273 } 1274 } 1275 nativeSetSize(width, Math.round((float) width * h / w), w, scale, 1276 w, h); 1277 } else { 1278 nativeSetSize(w, h, w, scale, w, h); 1279 } 1280 // Remember the current width and height 1281 boolean needInvalidate = (mCurrentViewWidth == 0); 1282 mCurrentViewWidth = w; 1283 mCurrentViewHeight = h; 1284 if (needInvalidate) { 1285 // ensure {@link #webkitDraw} is called as we were blocking in 1286 // {@link #contentDraw} when mCurrentViewWidth is 0 1287 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged"); 1288 contentDraw(); 1289 } 1290 mEventHub.sendMessage(Message.obtain(null, 1291 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY)); 1292 } 1293 1294 private void sendUpdateTextEntry() { 1295 if (mWebView != null) { 1296 Message.obtain(mWebView.mPrivateHandler, 1297 WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget(); 1298 } 1299 } 1300 1301 // Used to avoid posting more than one draw message. 1302 private boolean mDrawIsScheduled; 1303 1304 // Used to avoid posting more than one split picture message. 1305 private boolean mSplitPictureIsScheduled; 1306 1307 // Used to suspend drawing. 1308 private boolean mDrawIsPaused; 1309 1310 // Used to end scale+scroll mode, accessed by both threads 1311 boolean mEndScaleZoom = false; 1312 1313 public class DrawData { 1314 public DrawData() { 1315 mInvalRegion = new Region(); 1316 mWidthHeight = new Point(); 1317 } 1318 public Region mInvalRegion; 1319 public Point mViewPoint; 1320 public Point mWidthHeight; 1321 } 1322 1323 private void webkitDraw() { 1324 mDrawIsScheduled = false; 1325 DrawData draw = new DrawData(); 1326 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); 1327 if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight) 1328 == false) { 1329 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort"); 1330 return; 1331 } 1332 if (mWebView != null) { 1333 // Send the native view size that was used during the most recent 1334 // layout. 1335 draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight); 1336 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); 1337 Message.obtain(mWebView.mPrivateHandler, 1338 WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); 1339 if (mWebkitScrollX != 0 || mWebkitScrollY != 0) { 1340 // as we have the new picture, try to sync the scroll position 1341 Message.obtain(mWebView.mPrivateHandler, 1342 WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX, 1343 mWebkitScrollY).sendToTarget(); 1344 mWebkitScrollX = mWebkitScrollY = 0; 1345 } 1346 // nativeSnapToAnchor() needs to be called after NEW_PICTURE_MSG_ID 1347 // is sent, so that scroll will be based on the new content size. 1348 nativeSnapToAnchor(); 1349 } 1350 } 1351 1352 /////////////////////////////////////////////////////////////////////////// 1353 // These are called from the UI thread, not our thread 1354 1355 static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG | 1356 Paint.DITHER_FLAG | 1357 Paint.SUBPIXEL_TEXT_FLAG; 1358 static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG | 1359 Paint.DITHER_FLAG; 1360 1361 final DrawFilter mZoomFilter = 1362 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG); 1363 final DrawFilter mScrollFilter = 1364 new PaintFlagsDrawFilter(SCROLL_BITS, 0); 1365 1366 /* package */ void drawContentPicture(Canvas canvas, int color, 1367 boolean animatingZoom, 1368 boolean animatingScroll) { 1369 DrawFilter df = null; 1370 if (animatingZoom) { 1371 df = mZoomFilter; 1372 } else if (animatingScroll) { 1373 df = mScrollFilter; 1374 } 1375 canvas.setDrawFilter(df); 1376 boolean tookTooLong = nativeDrawContent(canvas, color); 1377 canvas.setDrawFilter(null); 1378 if (tookTooLong && mSplitPictureIsScheduled == false) { 1379 mSplitPictureIsScheduled = true; 1380 sendMessage(EventHub.SPLIT_PICTURE_SET); 1381 } 1382 } 1383 1384 /* package */ boolean pictureReady() { 1385 return nativePictureReady(); 1386 } 1387 1388 /*package*/ Picture copyContentPicture() { 1389 Picture result = new Picture(); 1390 nativeCopyContentToPicture(result); 1391 return result; 1392 } 1393 1394 static void pauseUpdate(WebViewCore core) { 1395 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1396 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1397 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1398 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1399 .obtainMessage(WebCoreThread.REDUCE_PRIORITY)); 1400 // Note: there is one possible failure mode. If pauseUpdate() is called 1401 // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out 1402 // of the queue and about to be executed. mDrawIsScheduled may be set to 1403 // false in webkitDraw(). So update won't be blocked. But at least the 1404 // webcore thread priority is still lowered. 1405 if (core != null) { 1406 synchronized (core) { 1407 core.mDrawIsPaused = true; 1408 core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW); 1409 } 1410 } 1411 } 1412 1413 static void resumeUpdate(WebViewCore core) { 1414 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1415 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1416 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1417 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1418 .obtainMessage(WebCoreThread.RESUME_PRIORITY)); 1419 if (core != null) { 1420 synchronized (core) { 1421 core.mDrawIsScheduled = false; 1422 core.mDrawIsPaused = false; 1423 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate"); 1424 core.contentDraw(); 1425 } 1426 } 1427 } 1428 1429 static void startCacheTransaction() { 1430 sWebCoreHandler.sendMessage(sWebCoreHandler 1431 .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER)); 1432 } 1433 1434 static void endCacheTransaction() { 1435 sWebCoreHandler.sendMessage(sWebCoreHandler 1436 .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER)); 1437 } 1438 1439 ////////////////////////////////////////////////////////////////////////// 1440 1441 private void restoreState(int index) { 1442 WebBackForwardList list = mCallbackProxy.getBackForwardList(); 1443 int size = list.getSize(); 1444 for (int i = 0; i < size; i++) { 1445 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); 1446 } 1447 mBrowserFrame.mLoadInitFromJava = true; 1448 list.restoreIndex(mBrowserFrame.mNativeFrame, index); 1449 mBrowserFrame.mLoadInitFromJava = false; 1450 } 1451 1452 //------------------------------------------------------------------------- 1453 // Implement abstract methods in WebViewCore, native WebKit callback part 1454 //------------------------------------------------------------------------- 1455 1456 // called from JNI or WebView thread 1457 /* package */ void contentDraw() { 1458 // don't update the Picture until we have an initial width and finish 1459 // the first layout 1460 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { 1461 return; 1462 } 1463 // only fire an event if this is our first request 1464 synchronized (this) { 1465 if (mDrawIsPaused || mDrawIsScheduled) { 1466 return; 1467 } 1468 mDrawIsScheduled = true; 1469 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 1470 } 1471 } 1472 1473 // called by JNI 1474 private void contentScrollBy(int dx, int dy, boolean animate) { 1475 if (!mBrowserFrame.firstLayoutDone()) { 1476 // Will this happen? If yes, we need to do something here. 1477 return; 1478 } 1479 if (mWebView != null) { 1480 Message msg = Message.obtain(mWebView.mPrivateHandler, 1481 WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate)); 1482 if (mDrawIsScheduled) { 1483 mEventHub.sendMessage(Message.obtain(null, 1484 EventHub.MESSAGE_RELAY, msg)); 1485 } else { 1486 msg.sendToTarget(); 1487 } 1488 } 1489 } 1490 1491 // called by JNI 1492 private void contentScrollTo(int x, int y) { 1493 if (!mBrowserFrame.firstLayoutDone()) { 1494 /* 1495 * WebKit restore state will be called before didFirstLayout(), 1496 * remember the position as it has to be applied after restoring 1497 * zoom factor which is controlled by screenWidth. 1498 */ 1499 mRestoredX = x; 1500 mRestoredY = y; 1501 return; 1502 } 1503 if (mWebView != null) { 1504 Message msg = Message.obtain(mWebView.mPrivateHandler, 1505 WebView.SCROLL_TO_MSG_ID, x, y); 1506 if (mDrawIsScheduled) { 1507 mEventHub.sendMessage(Message.obtain(null, 1508 EventHub.MESSAGE_RELAY, msg)); 1509 } else { 1510 msg.sendToTarget(); 1511 } 1512 } 1513 } 1514 1515 // called by JNI 1516 private void contentSpawnScrollTo(int x, int y) { 1517 if (!mBrowserFrame.firstLayoutDone()) { 1518 /* 1519 * WebKit restore state will be called before didFirstLayout(), 1520 * remember the position as it has to be applied after restoring 1521 * zoom factor which is controlled by screenWidth. 1522 */ 1523 mRestoredX = x; 1524 mRestoredY = y; 1525 return; 1526 } 1527 if (mWebView != null) { 1528 Message msg = Message.obtain(mWebView.mPrivateHandler, 1529 WebView.SPAWN_SCROLL_TO_MSG_ID, x, y); 1530 if (mDrawIsScheduled) { 1531 mEventHub.sendMessage(Message.obtain(null, 1532 EventHub.MESSAGE_RELAY, msg)); 1533 } else { 1534 msg.sendToTarget(); 1535 } 1536 } 1537 } 1538 1539 // called by JNI 1540 private void sendMarkNodeInvalid(int node) { 1541 if (mWebView != null) { 1542 Message.obtain(mWebView.mPrivateHandler, 1543 WebView.MARK_NODE_INVALID_ID, node, 0).sendToTarget(); 1544 } 1545 } 1546 1547 // called by JNI 1548 private void sendNotifyProgressFinished() { 1549 sendUpdateTextEntry(); 1550 // as CacheManager can behave based on database transaction, we need to 1551 // call tick() to trigger endTransaction 1552 sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER); 1553 sWebCoreHandler.sendMessage(sWebCoreHandler 1554 .obtainMessage(WebCoreThread.CACHE_TICKER)); 1555 contentDraw(); 1556 } 1557 1558 // called by JNI 1559 private void sendRecomputeFocus() { 1560 if (mWebView != null) { 1561 Message.obtain(mWebView.mPrivateHandler, 1562 WebView.RECOMPUTE_FOCUS_MSG_ID).sendToTarget(); 1563 } 1564 } 1565 1566 /* Called by JNI. The coordinates are in doc coordinates, so they need to 1567 be scaled before they can be used by the view system, which happens 1568 in WebView since it (and its thread) know the current scale factor. 1569 */ 1570 private void sendViewInvalidate(int left, int top, int right, int bottom) { 1571 if (mWebView != null) { 1572 Message.obtain(mWebView.mPrivateHandler, 1573 WebView.INVAL_RECT_MSG_ID, 1574 new Rect(left, top, right, bottom)).sendToTarget(); 1575 } 1576 } 1577 1578 /* package */ WebView getWebView() { 1579 return mWebView; 1580 } 1581 1582 private native void setViewportSettingsFromNative(); 1583 1584 // called by JNI 1585 private void didFirstLayout(boolean standardLoad) { 1586 // Trick to ensure that the Picture has the exact height for the content 1587 // by forcing to layout with 0 height after the page is ready, which is 1588 // indicated by didFirstLayout. This is essential to get rid of the 1589 // white space in the GMail which uses WebView for message view. 1590 if (mWebView != null && mWebView.mHeightCanMeasure) { 1591 mWebView.mLastHeightSent = 0; 1592 // Send a negative scale to indicate that WebCore should reuse the 1593 // current scale 1594 mEventHub.sendMessage(Message.obtain(null, 1595 EventHub.VIEW_SIZE_CHANGED, mWebView.mLastWidthSent, 1596 mWebView.mLastHeightSent, -1.0f)); 1597 } 1598 1599 mBrowserFrame.didFirstLayout(); 1600 1601 // reset the scroll position as it is a new page now 1602 mWebkitScrollX = mWebkitScrollY = 0; 1603 1604 // set the viewport settings from WebKit 1605 setViewportSettingsFromNative(); 1606 1607 // infer the values if they are not defined. 1608 if (mViewportWidth == 0) { 1609 if (mViewportInitialScale == 0) { 1610 mViewportInitialScale = 100; 1611 } 1612 if (mViewportMinimumScale == 0) { 1613 mViewportMinimumScale = 100; 1614 } 1615 } 1616 if (mViewportUserScalable == false) { 1617 mViewportInitialScale = 100; 1618 mViewportMinimumScale = 100; 1619 mViewportMaximumScale = 100; 1620 } 1621 if (mViewportMinimumScale > mViewportInitialScale) { 1622 if (mViewportInitialScale == 0) { 1623 mViewportInitialScale = mViewportMinimumScale; 1624 } else { 1625 mViewportMinimumScale = mViewportInitialScale; 1626 } 1627 } 1628 if (mViewportMaximumScale > 0) { 1629 if (mViewportMaximumScale < mViewportInitialScale) { 1630 mViewportMaximumScale = mViewportInitialScale; 1631 } else if (mViewportInitialScale == 0) { 1632 mViewportInitialScale = mViewportMaximumScale; 1633 } 1634 } 1635 if (mViewportWidth < 0 && mViewportInitialScale == 100) { 1636 mViewportWidth = 0; 1637 } 1638 1639 // now notify webview 1640 if (mWebView != null) { 1641 HashMap scaleLimit = new HashMap(); 1642 scaleLimit.put("minScale", mViewportMinimumScale); 1643 scaleLimit.put("maxScale", mViewportMaximumScale); 1644 1645 if (mRestoredScale > 0) { 1646 Message.obtain(mWebView.mPrivateHandler, 1647 WebView.DID_FIRST_LAYOUT_MSG_ID, mRestoredScale, 0, 1648 scaleLimit).sendToTarget(); 1649 mRestoredScale = 0; 1650 } else { 1651 // if standardLoad is true, use mViewportInitialScale, otherwise 1652 // pass -1 to the WebView to indicate no change of the scale. 1653 Message.obtain(mWebView.mPrivateHandler, 1654 WebView.DID_FIRST_LAYOUT_MSG_ID, 1655 standardLoad ? mViewportInitialScale : -1, 1656 mViewportWidth, scaleLimit).sendToTarget(); 1657 } 1658 1659 // force an early draw for quick feedback after the first layout 1660 if (mCurrentViewWidth != 0) { 1661 synchronized (this) { 1662 if (mDrawIsScheduled) { 1663 mEventHub.removeMessages(EventHub.WEBKIT_DRAW); 1664 } 1665 mDrawIsScheduled = true; 1666 // if no restored offset, move the new page to (0, 0) 1667 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 1668 EventHub.MESSAGE_RELAY, Message.obtain( 1669 mWebView.mPrivateHandler, 1670 WebView.SCROLL_TO_MSG_ID, mRestoredX, 1671 mRestoredY))); 1672 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 1673 EventHub.WEBKIT_DRAW)); 1674 } 1675 } 1676 1677 // reset restored offset 1678 mRestoredX = mRestoredY = 0; 1679 } 1680 } 1681 1682 // called by JNI 1683 private void restoreScale(int scale) { 1684 if (mBrowserFrame.firstLayoutDone() == false) { 1685 mRestoredScale = scale; 1686 } 1687 } 1688 1689 // called by JNI 1690 private void needTouchEvents(boolean need) { 1691 if (mWebView != null) { 1692 Message.obtain(mWebView.mPrivateHandler, 1693 WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0) 1694 .sendToTarget(); 1695 } 1696 } 1697 1698 // called by JNI 1699 private void updateTextfield(int ptr, boolean changeToPassword, 1700 String text, int textGeneration) { 1701 if (mWebView != null) { 1702 Message msg = Message.obtain(mWebView.mPrivateHandler, 1703 WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 1704 textGeneration, text); 1705 msg.getData().putBoolean("password", changeToPassword); 1706 msg.sendToTarget(); 1707 } 1708 } 1709 1710 // these must be in document space (i.e. not scaled/zoomed). 1711 private native void nativeSetScrollOffset(int dx, int dy); 1712 1713 private native void nativeSetGlobalBounds(int x, int y, int w, int h); 1714 1715 // called by JNI 1716 private void requestListBox(String[] array, boolean[] enabledArray, 1717 int[] selectedArray) { 1718 if (mWebView != null) { 1719 mWebView.requestListBox(array, enabledArray, selectedArray); 1720 } 1721 } 1722 1723 // called by JNI 1724 private void requestListBox(String[] array, boolean[] enabledArray, 1725 int selection) { 1726 if (mWebView != null) { 1727 mWebView.requestListBox(array, enabledArray, selection); 1728 } 1729 1730 } 1731 1732 private native void nativePause(); 1733 private native void nativeResume(); 1734} 1735