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