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