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