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