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