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