WebViewCore.java revision 6078399d29135d3fcac7d5dbd9ebb0c54a44f279
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.content.Intent; 21import android.content.pm.PackageManager.NameNotFoundException; 22import android.database.Cursor; 23import android.graphics.Canvas; 24import android.graphics.DrawFilter; 25import android.graphics.Paint; 26import android.graphics.PaintFlagsDrawFilter; 27import android.graphics.Picture; 28import android.graphics.Point; 29import android.graphics.Rect; 30import android.graphics.Region; 31import android.net.Uri; 32import android.os.Handler; 33import android.os.Looper; 34import android.os.Message; 35import android.os.Process; 36import android.provider.Browser; 37import android.provider.OpenableColumns; 38import android.util.Log; 39import android.util.SparseBooleanArray; 40import android.view.KeyEvent; 41import android.view.SurfaceHolder; 42import android.view.SurfaceView; 43import android.view.View; 44 45import java.util.ArrayList; 46import java.util.Collection; 47import java.util.Map; 48import java.util.Set; 49 50import junit.framework.Assert; 51 52final class WebViewCore { 53 54 private static final String LOGTAG = "webcore"; 55 56 static { 57 // Load libwebcore during static initialization. This happens in the 58 // zygote process so it will be shared read-only across all app 59 // processes. 60 System.loadLibrary("webcore"); 61 } 62 63 /* 64 * WebViewCore always executes in the same thread as the native webkit. 65 */ 66 67 // The WebView that corresponds to this WebViewCore. 68 private WebView mWebView; 69 // Proxy for handling callbacks from native code 70 private final CallbackProxy mCallbackProxy; 71 // Settings object for maintaining all settings 72 private final WebSettings mSettings; 73 // Context for initializing the BrowserFrame with the proper assets. 74 private final Context mContext; 75 // The pointer to a native view object. 76 private int mNativeClass; 77 // The BrowserFrame is an interface to the native Frame component. 78 private BrowserFrame mBrowserFrame; 79 // Custom JS interfaces to add during the initialization. 80 private Map<String, Object> mJavascriptInterfaces; 81 /* 82 * range is from 200 to 10,000. 0 is a special value means device-width. -1 83 * means undefined. 84 */ 85 private int mViewportWidth = -1; 86 87 /* 88 * range is from 200 to 10,000. 0 is a special value means device-height. -1 89 * means undefined. 90 */ 91 private int mViewportHeight = -1; 92 93 /* 94 * scale in percent, range is from 1 to 1000. 0 means undefined. 95 */ 96 private int mViewportInitialScale = 0; 97 98 /* 99 * scale in percent, range is from 1 to 1000. 0 means undefined. 100 */ 101 private int mViewportMinimumScale = 0; 102 103 /* 104 * scale in percent, range is from 1 to 1000. 0 means undefined. 105 */ 106 private int mViewportMaximumScale = 0; 107 108 private boolean mViewportUserScalable = true; 109 110 /* 111 * range is from 70 to 400. 112 * 0 is a special value means device-dpi. The default scale factor will be 113 * always 100. 114 * -1 means undefined. The default scale factor will be 115 * WebView.DEFAULT_SCALE_PERCENT. 116 */ 117 private int mViewportDensityDpi = -1; 118 119 private int mRestoredScale = 0; 120 private int mRestoredScreenWidthScale = 0; 121 private int mRestoredX = 0; 122 private int mRestoredY = 0; 123 124 private int mWebkitScrollX = 0; 125 private int mWebkitScrollY = 0; 126 127 // The thread name used to identify the WebCore thread and for use in 128 // debugging other classes that require operation within the WebCore thread. 129 /* package */ static final String THREAD_NAME = "WebViewCoreThread"; 130 131 public WebViewCore(Context context, WebView w, CallbackProxy proxy, 132 Map<String, Object> javascriptInterfaces) { 133 // No need to assign this in the WebCore thread. 134 mCallbackProxy = proxy; 135 mWebView = w; 136 mJavascriptInterfaces = javascriptInterfaces; 137 // This context object is used to initialize the WebViewCore during 138 // subwindow creation. 139 mContext = context; 140 141 // We need to wait for the initial thread creation before sending 142 // a message to the WebCore thread. 143 // XXX: This is the only time the UI thread will wait for the WebCore 144 // thread! 145 synchronized (WebViewCore.class) { 146 if (sWebCoreHandler == null) { 147 // Create a global thread and start it. 148 Thread t = new Thread(new WebCoreThread()); 149 t.setName(THREAD_NAME); 150 t.start(); 151 try { 152 WebViewCore.class.wait(); 153 } catch (InterruptedException e) { 154 Log.e(LOGTAG, "Caught exception while waiting for thread " + 155 "creation."); 156 Log.e(LOGTAG, Log.getStackTraceString(e)); 157 } 158 } 159 } 160 // Create an EventHub to handle messages before and after the thread is 161 // ready. 162 mEventHub = new EventHub(); 163 // Create a WebSettings object for maintaining all settings 164 mSettings = new WebSettings(mContext, mWebView); 165 // The WebIconDatabase needs to be initialized within the UI thread so 166 // just request the instance here. 167 WebIconDatabase.getInstance(); 168 // Create the WebStorage singleton and the UI handler 169 WebStorage.getInstance().createUIHandler(); 170 // Create the UI handler for GeolocationPermissions 171 GeolocationPermissions.getInstance().createUIHandler(); 172 // Send a message to initialize the WebViewCore. 173 Message init = sWebCoreHandler.obtainMessage( 174 WebCoreThread.INITIALIZE, this); 175 sWebCoreHandler.sendMessage(init); 176 } 177 178 /* Initialize private data within the WebCore thread. 179 */ 180 private void initialize() { 181 /* Initialize our private BrowserFrame class to handle all 182 * frame-related functions. We need to create a new view which 183 * in turn creates a C level FrameView and attaches it to the frame. 184 */ 185 mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy, 186 mSettings, mJavascriptInterfaces); 187 mJavascriptInterfaces = null; 188 // Sync the native settings and also create the WebCore thread handler. 189 mSettings.syncSettingsAndCreateHandler(mBrowserFrame); 190 // Create the handler and transfer messages for the IconDatabase 191 WebIconDatabase.getInstance().createHandler(); 192 // Create the handler for WebStorage 193 WebStorage.getInstance().createHandler(); 194 // Create the handler for GeolocationPermissions. 195 GeolocationPermissions.getInstance().createHandler(); 196 // The transferMessages call will transfer all pending messages to the 197 // WebCore thread handler. 198 mEventHub.transferMessages(); 199 200 // Send a message back to WebView to tell it that we have set up the 201 // WebCore thread. 202 if (mWebView != null) { 203 Message.obtain(mWebView.mPrivateHandler, 204 WebView.WEBCORE_INITIALIZED_MSG_ID, 205 mNativeClass, 0).sendToTarget(); 206 } 207 208 } 209 210 /* Handle the initialization of WebViewCore during subwindow creation. This 211 * method is called from the WebCore thread but it is called before the 212 * INITIALIZE message can be handled. 213 */ 214 /* package */ void initializeSubwindow() { 215 // Go ahead and initialize the core components. 216 initialize(); 217 // Remove the INITIALIZE method so we don't try to initialize twice. 218 sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this); 219 } 220 221 /* Get the BrowserFrame component. This is used for subwindow creation and 222 * is called only from BrowserFrame in the WebCore thread. */ 223 /* package */ BrowserFrame getBrowserFrame() { 224 return mBrowserFrame; 225 } 226 227 //------------------------------------------------------------------------- 228 // Common methods 229 //------------------------------------------------------------------------- 230 231 /** 232 * Causes all timers to pause. This applies to all WebViews in the current 233 * app process. 234 */ 235 public static void pauseTimers() { 236 if (BrowserFrame.sJavaBridge == null) { 237 throw new IllegalStateException( 238 "No WebView has been created in this process!"); 239 } 240 BrowserFrame.sJavaBridge.pause(); 241 } 242 243 /** 244 * Resume all timers. This applies to all WebViews in the current process. 245 */ 246 public static void resumeTimers() { 247 if (BrowserFrame.sJavaBridge == null) { 248 throw new IllegalStateException( 249 "No WebView has been created in this process!"); 250 } 251 BrowserFrame.sJavaBridge.resume(); 252 } 253 254 public WebSettings getSettings() { 255 return mSettings; 256 } 257 258 /** 259 * Add an error message to the client's console. 260 * @param message The message to add 261 * @param lineNumber the line on which the error occurred 262 * @param sourceID the filename of the source that caused the error. 263 */ 264 protected void addMessageToConsole(String message, int lineNumber, String sourceID) { 265 mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID); 266 } 267 268 /** 269 * Invoke a javascript alert. 270 * @param message The message displayed in the alert. 271 */ 272 protected void jsAlert(String url, String message) { 273 mCallbackProxy.onJsAlert(url, message); 274 } 275 276 277 /** 278 * Called by JNI. Open a file chooser to upload a file. 279 * @return String version of the URI plus the name of the file. 280 * FIXME: Just return the URI here, and in FileSystem::pathGetFileName, call 281 * into Java to get the filename. 282 */ 283 private String openFileChooser() { 284 Uri uri = mCallbackProxy.openFileChooser(); 285 if (uri == null) return ""; 286 // Find out the name, and append it to the URI. 287 // Webkit will treat the name as the filename, and 288 // the URI as the path. The URI will be used 289 // in BrowserFrame to get the actual data. 290 Cursor cursor = mContext.getContentResolver().query( 291 uri, 292 new String[] { OpenableColumns.DISPLAY_NAME }, 293 null, 294 null, 295 null); 296 String name = ""; 297 if (cursor != null) { 298 try { 299 if (cursor.moveToNext()) { 300 name = cursor.getString(0); 301 } 302 } finally { 303 cursor.close(); 304 } 305 } 306 return uri.toString() + "/" + name; 307 } 308 309 /** 310 * Notify the browser that the origin has exceeded it's database quota. 311 * @param url The URL that caused the overflow. 312 * @param databaseIdentifier The identifier of the database. 313 * @param currentQuota The current quota for the origin. 314 * @param estimatedSize The estimated size of the database. 315 */ 316 protected void exceededDatabaseQuota(String url, 317 String databaseIdentifier, 318 long currentQuota, 319 long estimatedSize) { 320 // Inform the callback proxy of the quota overflow. Send an object 321 // that encapsulates a call to the nativeSetDatabaseQuota method to 322 // awaken the sleeping webcore thread when a decision from the 323 // client to allow or deny quota is available. 324 mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier, 325 currentQuota, estimatedSize, getUsedQuota(), 326 new WebStorage.QuotaUpdater() { 327 public void updateQuota(long quota) { 328 nativeSetNewStorageLimit(quota); 329 } 330 }); 331 } 332 333 /** 334 * Notify the browser that the appcache has exceeded its max size. 335 * @param spaceNeeded is the amount of disk space that would be needed 336 * in order for the last appcache operation to succeed. 337 */ 338 protected void reachedMaxAppCacheSize(long spaceNeeded) { 339 mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(), 340 new WebStorage.QuotaUpdater() { 341 public void updateQuota(long quota) { 342 nativeSetNewStorageLimit(quota); 343 } 344 }); 345 } 346 347 protected void populateVisitedLinks() { 348 ValueCallback callback = new ValueCallback<String[]>() { 349 public void onReceiveValue(String[] value) { 350 sendMessage(EventHub.POPULATE_VISITED_LINKS, (Object)value); 351 } 352 }; 353 mCallbackProxy.getVisitedHistory(callback); 354 } 355 356 /** 357 * Shows a prompt to ask the user to set the Geolocation permission state 358 * for the given origin. 359 * @param origin The origin for which Geolocation permissions are 360 * requested. 361 */ 362 protected void geolocationPermissionsShowPrompt(String origin) { 363 mCallbackProxy.onGeolocationPermissionsShowPrompt(origin, 364 new GeolocationPermissions.Callback() { 365 public void invoke(String origin, boolean allow, boolean remember) { 366 GeolocationPermissionsData data = new GeolocationPermissionsData(); 367 data.mOrigin = origin; 368 data.mAllow = allow; 369 data.mRemember = remember; 370 // Marshall to WebCore thread. 371 sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data); 372 } 373 }); 374 } 375 376 /** 377 * Hides the Geolocation permissions prompt. 378 */ 379 protected void geolocationPermissionsHidePrompt() { 380 mCallbackProxy.onGeolocationPermissionsHidePrompt(); 381 } 382 383 /** 384 * Invoke a javascript confirm dialog. 385 * @param message The message displayed in the dialog. 386 * @return True if the user confirmed or false if the user cancelled. 387 */ 388 protected boolean jsConfirm(String url, String message) { 389 return mCallbackProxy.onJsConfirm(url, message); 390 } 391 392 /** 393 * Invoke a javascript prompt dialog. 394 * @param message The message to be displayed in the dialog. 395 * @param defaultValue The default value in the prompt input. 396 * @return The input from the user or null to indicate the user cancelled 397 * the dialog. 398 */ 399 protected String jsPrompt(String url, String message, String defaultValue) { 400 return mCallbackProxy.onJsPrompt(url, message, defaultValue); 401 } 402 403 /** 404 * Invoke a javascript before unload dialog. 405 * @param url The url that is requesting the dialog. 406 * @param message The message displayed in the dialog. 407 * @return True if the user confirmed or false if the user cancelled. False 408 * will cancel the navigation. 409 */ 410 protected boolean jsUnload(String url, String message) { 411 return mCallbackProxy.onJsBeforeUnload(url, message); 412 } 413 414 /** 415 * 416 * Callback to notify that a JavaScript execution timeout has occured. 417 * @return True if the JavaScript execution should be interrupted. False 418 * will continue the execution. 419 */ 420 protected boolean jsInterrupt() { 421 return mCallbackProxy.onJsTimeout(); 422 } 423 424 //------------------------------------------------------------------------- 425 // JNI methods 426 //------------------------------------------------------------------------- 427 428 static native String nativeFindAddress(String addr, boolean caseInsensitive); 429 430 /** 431 * Empty the picture set. 432 */ 433 private native void nativeClearContent(); 434 435 /** 436 * Create a flat picture from the set of pictures. 437 */ 438 private native void nativeCopyContentToPicture(Picture picture); 439 440 /** 441 * Draw the picture set with a background color. Returns true 442 * if some individual picture took too long to draw and can be 443 * split into parts. Called from the UI thread. 444 */ 445 private native boolean nativeDrawContent(Canvas canvas, int color); 446 447 /** 448 * check to see if picture is blank and in progress 449 */ 450 private native boolean nativePictureReady(); 451 452 /** 453 * Redraw a portion of the picture set. The Point wh returns the 454 * width and height of the overall picture. 455 */ 456 private native boolean nativeRecordContent(Region invalRegion, Point wh); 457 458 private native boolean nativeFocusBoundsChanged(); 459 460 /** 461 * Splits slow parts of the picture set. Called from the webkit 462 * thread after nativeDrawContent returns true. 463 */ 464 private native void nativeSplitContent(); 465 466 private native boolean nativeKey(int keyCode, int unichar, 467 int repeatCount, boolean isShift, boolean isAlt, boolean isSym, 468 boolean isDown); 469 470 private native void nativeClick(int framePtr, int nodePtr); 471 472 private native void nativeSendListBoxChoices(boolean[] choices, int size); 473 474 private native void nativeSendListBoxChoice(int choice); 475 476 /* Tell webkit what its width and height are, for the purposes 477 of layout/line-breaking. These coordinates are in document space, 478 which is the same as View coords unless we have zoomed the document 479 (see nativeSetZoom). 480 screenWidth is used by layout to wrap column around. If viewport uses 481 fixed size, screenWidth can be different from width with zooming. 482 should this be called nativeSetViewPortSize? 483 */ 484 private native void nativeSetSize(int width, int height, int screenWidth, 485 float scale, int realScreenWidth, int screenHeight, 486 boolean ignoreHeight); 487 488 private native int nativeGetContentMinPrefWidth(); 489 490 // Start: functions that deal with text editing 491 private native void nativeReplaceTextfieldText( 492 int oldStart, int oldEnd, String replace, int newStart, int newEnd, 493 int textGeneration); 494 495 private native void passToJs(int gen, 496 String currentText, int keyCode, int keyValue, boolean down, 497 boolean cap, boolean fn, boolean sym); 498 499 private native void nativeSetFocusControllerActive(boolean active); 500 501 private native void nativeSaveDocumentState(int frame); 502 503 private native void nativeMoveFocus(int framePtr, int nodePointer); 504 private native void nativeMoveMouse(int framePtr, int x, int y); 505 506 private native void nativeMoveMouseIfLatest(int moveGeneration, 507 int framePtr, int x, int y); 508 509 private native String nativeRetrieveHref(int framePtr, int nodePtr); 510 private native String nativeRetrieveAnchorText(int framePtr, int nodePtr); 511 512 private native void nativeTouchUp(int touchGeneration, 513 int framePtr, int nodePtr, int x, int y); 514 515 private native int nativeHandleTouchEvent(int action, int x, int y, long time); 516 517 private native void nativeUpdateFrameCache(); 518 519 private native void nativeSetBackgroundColor(int color); 520 521 private native void nativeDumpDomTree(boolean useFile); 522 523 private native void nativeDumpRenderTree(boolean useFile); 524 525 private native void nativeDumpNavTree(); 526 527 private native void nativeSetJsFlags(String flags); 528 529 /** 530 * Delete text from start to end in the focused textfield. If there is no 531 * focus, or if start == end, silently fail. If start and end are out of 532 * order, swap them. 533 * @param start Beginning of selection to delete. 534 * @param end End of selection to delete. 535 * @param textGeneration Text generation number when delete was pressed. 536 */ 537 private native void nativeDeleteSelection(int start, int end, 538 int textGeneration); 539 540 /** 541 * Set the selection to (start, end) in the focused textfield. If start and 542 * end are out of order, swap them. 543 * @param start Beginning of selection. 544 * @param end End of selection. 545 */ 546 private native void nativeSetSelection(int start, int end); 547 548 private native String nativeGetSelection(Region sel); 549 550 // Register a scheme to be treated as local scheme so that it can access 551 // local asset files for resources 552 private native void nativeRegisterURLSchemeAsLocal(String scheme); 553 554 /* 555 * Inform webcore that the user has decided whether to allow or deny new 556 * quota for the current origin or more space for the app cache, and that 557 * the main thread should wake up now. 558 * @param limit Is the new quota for an origin or new app cache max size. 559 */ 560 private native void nativeSetNewStorageLimit(long limit); 561 562 /** 563 * Provide WebCore with a Geolocation permission state for the specified 564 * origin. 565 * @param origin The origin for which Geolocation permissions are provided. 566 * @param allow Whether Geolocation permissions are allowed. 567 * @param remember Whether this decision should be remembered beyond the 568 * life of the current page. 569 */ 570 private native void nativeGeolocationPermissionsProvide(String origin, boolean allow, boolean remember); 571 572 /** 573 * Provide WebCore with the previously visted links from the history database 574 */ 575 private native void nativeProvideVisitedHistory(String[] history); 576 577 // EventHub for processing messages 578 private final EventHub mEventHub; 579 // WebCore thread handler 580 private static Handler sWebCoreHandler; 581 // Class for providing Handler creation inside the WebCore thread. 582 private static class WebCoreThread implements Runnable { 583 // Message id for initializing a new WebViewCore. 584 private static final int INITIALIZE = 0; 585 private static final int REDUCE_PRIORITY = 1; 586 private static final int RESUME_PRIORITY = 2; 587 private static final int CACHE_TICKER = 3; 588 private static final int BLOCK_CACHE_TICKER = 4; 589 private static final int RESUME_CACHE_TICKER = 5; 590 591 private static final int CACHE_TICKER_INTERVAL = 60 * 1000; // 1 minute 592 593 private static boolean mCacheTickersBlocked = true; 594 595 public void run() { 596 Looper.prepare(); 597 Assert.assertNull(sWebCoreHandler); 598 synchronized (WebViewCore.class) { 599 sWebCoreHandler = new Handler() { 600 @Override 601 public void handleMessage(Message msg) { 602 switch (msg.what) { 603 case INITIALIZE: 604 WebViewCore core = (WebViewCore) msg.obj; 605 core.initialize(); 606 break; 607 608 case REDUCE_PRIORITY: 609 // 3 is an adjustable number. 610 Process.setThreadPriority( 611 Process.THREAD_PRIORITY_DEFAULT + 3 * 612 Process.THREAD_PRIORITY_LESS_FAVORABLE); 613 break; 614 615 case RESUME_PRIORITY: 616 Process.setThreadPriority( 617 Process.THREAD_PRIORITY_DEFAULT); 618 break; 619 620 case CACHE_TICKER: 621 if (!mCacheTickersBlocked) { 622 CacheManager.endCacheTransaction(); 623 CacheManager.startCacheTransaction(); 624 sendMessageDelayed( 625 obtainMessage(CACHE_TICKER), 626 CACHE_TICKER_INTERVAL); 627 } 628 break; 629 630 case BLOCK_CACHE_TICKER: 631 if (CacheManager.endCacheTransaction()) { 632 mCacheTickersBlocked = true; 633 } 634 break; 635 636 case RESUME_CACHE_TICKER: 637 if (CacheManager.startCacheTransaction()) { 638 mCacheTickersBlocked = false; 639 } 640 break; 641 } 642 } 643 }; 644 WebViewCore.class.notify(); 645 } 646 Looper.loop(); 647 } 648 } 649 650 static class BaseUrlData { 651 String mBaseUrl; 652 String mData; 653 String mMimeType; 654 String mEncoding; 655 String mFailUrl; 656 } 657 658 static class CursorData { 659 CursorData() {} 660 CursorData(int frame, int node, int x, int y) { 661 mFrame = frame; 662 mNode = node; 663 mX = x; 664 mY = y; 665 } 666 int mMoveGeneration; 667 int mFrame; 668 int mNode; 669 int mX; 670 int mY; 671 } 672 673 static class JSInterfaceData { 674 Object mObject; 675 String mInterfaceName; 676 } 677 678 static class JSKeyData { 679 String mCurrentText; 680 KeyEvent mEvent; 681 } 682 683 static class MotionUpData { 684 int mFrame; 685 int mNode; 686 Rect mBounds; 687 int mX; 688 int mY; 689 } 690 691 static class PostUrlData { 692 String mUrl; 693 byte[] mPostData; 694 } 695 696 static class ReplaceTextData { 697 String mReplace; 698 int mNewStart; 699 int mNewEnd; 700 int mTextGeneration; 701 } 702 703 static class TextSelectionData { 704 public TextSelectionData(int start, int end) { 705 mStart = start; 706 mEnd = end; 707 } 708 int mStart; 709 int mEnd; 710 } 711 712 static class TouchUpData { 713 int mMoveGeneration; 714 int mFrame; 715 int mNode; 716 int mX; 717 int mY; 718 } 719 720 // mAction of TouchEventData can be MotionEvent.getAction() which uses the 721 // last two bytes or one of the following values 722 static final int ACTION_LONGPRESS = 0x100; 723 static final int ACTION_DOUBLETAP = 0x200; 724 725 static class TouchEventData { 726 int mAction; 727 int mX; 728 int mY; 729 long mEventTime; 730 } 731 732 static class GeolocationPermissionsData { 733 String mOrigin; 734 boolean mAllow; 735 boolean mRemember; 736 } 737 738 static class PluginFullScreenData { 739 View mView; 740 int mNpp; 741 int mDocX; 742 int mDocY; 743 int mDocWidth; 744 int mDocHeight; 745 } 746 747 static final String[] HandlerDebugString = { 748 "REQUEST_LABEL", // 97 749 "UPDATE_FRAME_CACHE_IF_LOADING", // = 98 750 "SCROLL_TEXT_INPUT", // = 99 751 "LOAD_URL", // = 100; 752 "STOP_LOADING", // = 101; 753 "RELOAD", // = 102; 754 "KEY_DOWN", // = 103; 755 "KEY_UP", // = 104; 756 "VIEW_SIZE_CHANGED", // = 105; 757 "GO_BACK_FORWARD", // = 106; 758 "SET_SCROLL_OFFSET", // = 107; 759 "RESTORE_STATE", // = 108; 760 "PAUSE_TIMERS", // = 109; 761 "RESUME_TIMERS", // = 110; 762 "CLEAR_CACHE", // = 111; 763 "CLEAR_HISTORY", // = 112; 764 "SET_SELECTION", // = 113; 765 "REPLACE_TEXT", // = 114; 766 "PASS_TO_JS", // = 115; 767 "SET_GLOBAL_BOUNDS", // = 116; 768 "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117; 769 "CLICK", // = 118; 770 "SET_NETWORK_STATE", // = 119; 771 "DOC_HAS_IMAGES", // = 120; 772 "121", // = 121; 773 "DELETE_SELECTION", // = 122; 774 "LISTBOX_CHOICES", // = 123; 775 "SINGLE_LISTBOX_CHOICE", // = 124; 776 "MESSAGE_RELAY", // = 125; 777 "SET_BACKGROUND_COLOR", // = 126; 778 "SET_MOVE_FOCUS", // = 127 779 "SAVE_DOCUMENT_STATE", // = 128; 780 "GET_SELECTION", // = 129; 781 "WEBKIT_DRAW", // = 130; 782 "SYNC_SCROLL", // = 131; 783 "POST_URL", // = 132; 784 "SPLIT_PICTURE_SET", // = 133; 785 "CLEAR_CONTENT", // = 134; 786 "SET_MOVE_MOUSE", // = 135; 787 "SET_MOVE_MOUSE_IF_LATEST", // = 136; 788 "REQUEST_CURSOR_HREF", // = 137; 789 "ADD_JS_INTERFACE", // = 138; 790 "LOAD_DATA", // = 139; 791 "TOUCH_UP", // = 140; 792 "TOUCH_EVENT", // = 141; 793 "SET_ACTIVE", // = 142; 794 "ON_PAUSE", // = 143 795 "ON_RESUME", // = 144 796 "FREE_MEMORY", // = 145 797 "VALID_NODE_BOUNDS", // = 146 798 }; 799 800 class EventHub { 801 // Message Ids 802 static final int REQUEST_LABEL = 97; 803 static final int UPDATE_FRAME_CACHE_IF_LOADING = 98; 804 static final int SCROLL_TEXT_INPUT = 99; 805 static final int LOAD_URL = 100; 806 static final int STOP_LOADING = 101; 807 static final int RELOAD = 102; 808 static final int KEY_DOWN = 103; 809 static final int KEY_UP = 104; 810 static final int VIEW_SIZE_CHANGED = 105; 811 static final int GO_BACK_FORWARD = 106; 812 static final int SET_SCROLL_OFFSET = 107; 813 static final int RESTORE_STATE = 108; 814 static final int PAUSE_TIMERS = 109; 815 static final int RESUME_TIMERS = 110; 816 static final int CLEAR_CACHE = 111; 817 static final int CLEAR_HISTORY = 112; 818 static final int SET_SELECTION = 113; 819 static final int REPLACE_TEXT = 114; 820 static final int PASS_TO_JS = 115; 821 static final int SET_GLOBAL_BOUNDS = 116; 822 static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117; 823 static final int CLICK = 118; 824 static final int SET_NETWORK_STATE = 119; 825 static final int DOC_HAS_IMAGES = 120; 826 static final int DELETE_SELECTION = 122; 827 static final int LISTBOX_CHOICES = 123; 828 static final int SINGLE_LISTBOX_CHOICE = 124; 829 static final int MESSAGE_RELAY = 125; 830 static final int SET_BACKGROUND_COLOR = 126; 831 static final int SET_MOVE_FOCUS = 127; 832 static final int SAVE_DOCUMENT_STATE = 128; 833 static final int GET_SELECTION = 129; 834 static final int WEBKIT_DRAW = 130; 835 static final int SYNC_SCROLL = 131; 836 static final int POST_URL = 132; 837 static final int SPLIT_PICTURE_SET = 133; 838 static final int CLEAR_CONTENT = 134; 839 840 // UI nav messages 841 static final int SET_MOVE_MOUSE = 135; 842 static final int SET_MOVE_MOUSE_IF_LATEST = 136; 843 static final int REQUEST_CURSOR_HREF = 137; 844 static final int ADD_JS_INTERFACE = 138; 845 static final int LOAD_DATA = 139; 846 847 // motion 848 static final int TOUCH_UP = 140; 849 // message used to pass UI touch events to WebCore 850 static final int TOUCH_EVENT = 141; 851 852 // Used to tell the focus controller not to draw the blinking cursor, 853 // based on whether the WebView has focus and whether the WebView's 854 // cursor matches the webpage's focus. 855 static final int SET_ACTIVE = 142; 856 857 // lifecycle activities for just this DOM (unlike pauseTimers, which 858 // is global) 859 static final int ON_PAUSE = 143; 860 static final int ON_RESUME = 144; 861 static final int FREE_MEMORY = 145; 862 static final int VALID_NODE_BOUNDS = 146; 863 864 // Network-based messaging 865 static final int CLEAR_SSL_PREF_TABLE = 150; 866 867 // Test harness messages 868 static final int REQUEST_EXT_REPRESENTATION = 160; 869 static final int REQUEST_DOC_AS_TEXT = 161; 870 871 // debugging 872 static final int DUMP_DOMTREE = 170; 873 static final int DUMP_RENDERTREE = 171; 874 static final int DUMP_NAVTREE = 172; 875 876 static final int SET_JS_FLAGS = 173; 877 // Geolocation 878 static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180; 879 880 static final int POPULATE_VISITED_LINKS = 181; 881 882 static final int HIDE_FULLSCREEN = 182; 883 884 // private message ids 885 private static final int DESTROY = 200; 886 887 // Private handler for WebCore messages. 888 private Handler mHandler; 889 // Message queue for containing messages before the WebCore thread is 890 // ready. 891 private ArrayList<Message> mMessages = new ArrayList<Message>(); 892 // Flag for blocking messages. This is used during DESTROY to avoid 893 // posting more messages to the EventHub or to WebView's event handler. 894 private boolean mBlockMessages; 895 896 private int mTid; 897 private int mSavedPriority; 898 899 /** 900 * Prevent other classes from creating an EventHub. 901 */ 902 private EventHub() {} 903 904 /** 905 * Transfer all messages to the newly created webcore thread handler. 906 */ 907 private void transferMessages() { 908 mTid = Process.myTid(); 909 mSavedPriority = Process.getThreadPriority(mTid); 910 911 mHandler = new Handler() { 912 @Override 913 public void handleMessage(Message msg) { 914 if (DebugFlags.WEB_VIEW_CORE) { 915 Log.v(LOGTAG, (msg.what < REQUEST_LABEL 916 || msg.what 917 > VALID_NODE_BOUNDS ? Integer.toString(msg.what) 918 : HandlerDebugString[msg.what 919 - REQUEST_LABEL]) 920 + " arg1=" + msg.arg1 + " arg2=" + msg.arg2 921 + " obj=" + msg.obj); 922 } 923 switch (msg.what) { 924 case WEBKIT_DRAW: 925 webkitDraw(); 926 break; 927 928 case DESTROY: 929 // Time to take down the world. Cancel all pending 930 // loads and destroy the native view and frame. 931 synchronized (WebViewCore.this) { 932 mBrowserFrame.destroy(); 933 mBrowserFrame = null; 934 mSettings.onDestroyed(); 935 mNativeClass = 0; 936 mWebView = null; 937 } 938 break; 939 940 case REQUEST_LABEL: 941 if (mWebView != null) { 942 int nodePointer = msg.arg2; 943 String label = nativeRequestLabel(msg.arg1, 944 nodePointer); 945 if (label != null && label.length() > 0) { 946 Message.obtain(mWebView.mPrivateHandler, 947 WebView.RETURN_LABEL, nodePointer, 948 0, label).sendToTarget(); 949 } 950 } 951 break; 952 953 case UPDATE_FRAME_CACHE_IF_LOADING: 954 nativeUpdateFrameCacheIfLoading(); 955 break; 956 957 case SCROLL_TEXT_INPUT: 958 nativeScrollFocusedTextInput( 959 ((Float) msg.obj).floatValue(), msg.arg1); 960 break; 961 962 case LOAD_URL: 963 loadUrl((String) msg.obj); 964 break; 965 966 case POST_URL: { 967 PostUrlData param = (PostUrlData) msg.obj; 968 mBrowserFrame.postUrl(param.mUrl, param.mPostData); 969 break; 970 } 971 case LOAD_DATA: 972 BaseUrlData loadParams = (BaseUrlData) msg.obj; 973 String baseUrl = loadParams.mBaseUrl; 974 if (baseUrl != null) { 975 int i = baseUrl.indexOf(':'); 976 if (i > 0) { 977 /* 978 * In 1.0, {@link 979 * WebView#loadDataWithBaseURL} can access 980 * local asset files as long as the data is 981 * valid. In the new WebKit, the restriction 982 * is tightened. To be compatible with 1.0, 983 * we automatically add the scheme of the 984 * baseUrl for local access as long as it is 985 * not http(s)/ftp(s)/about/javascript 986 */ 987 String scheme = baseUrl.substring(0, i); 988 if (!scheme.startsWith("http") && 989 !scheme.startsWith("ftp") && 990 !scheme.startsWith("about") && 991 !scheme.startsWith("javascript")) { 992 nativeRegisterURLSchemeAsLocal(scheme); 993 } 994 } 995 } 996 mBrowserFrame.loadData(baseUrl, 997 loadParams.mData, 998 loadParams.mMimeType, 999 loadParams.mEncoding, 1000 loadParams.mFailUrl); 1001 break; 1002 1003 case STOP_LOADING: 1004 // If the WebCore has committed the load, but not 1005 // finished the first layout yet, we need to set 1006 // first layout done to trigger the interpreted side sync 1007 // up with native side 1008 if (mBrowserFrame.committed() 1009 && !mBrowserFrame.firstLayoutDone()) { 1010 mBrowserFrame.didFirstLayout(); 1011 } 1012 // Do this after syncing up the layout state. 1013 stopLoading(); 1014 break; 1015 1016 case RELOAD: 1017 mBrowserFrame.reload(false); 1018 break; 1019 1020 case KEY_DOWN: 1021 key((KeyEvent) msg.obj, true); 1022 break; 1023 1024 case KEY_UP: 1025 key((KeyEvent) msg.obj, false); 1026 break; 1027 1028 case CLICK: 1029 nativeClick(msg.arg1, msg.arg2); 1030 break; 1031 1032 case VIEW_SIZE_CHANGED: { 1033 WebView.ViewSizeData data = 1034 (WebView.ViewSizeData) msg.obj; 1035 viewSizeChanged(data.mWidth, data.mHeight, 1036 data.mTextWrapWidth, data.mScale, 1037 data.mIgnoreHeight); 1038 break; 1039 } 1040 case SET_SCROLL_OFFSET: 1041 // note: these are in document coordinates 1042 // (inv-zoom) 1043 Point pt = (Point) msg.obj; 1044 nativeSetScrollOffset(msg.arg1, pt.x, pt.y); 1045 break; 1046 1047 case SET_GLOBAL_BOUNDS: 1048 Rect r = (Rect) msg.obj; 1049 nativeSetGlobalBounds(r.left, r.top, r.width(), 1050 r.height()); 1051 break; 1052 1053 case GO_BACK_FORWARD: 1054 // If it is a standard load and the load is not 1055 // committed yet, we interpret BACK as RELOAD 1056 if (!mBrowserFrame.committed() && msg.arg1 == -1 && 1057 (mBrowserFrame.loadType() == 1058 BrowserFrame.FRAME_LOADTYPE_STANDARD)) { 1059 mBrowserFrame.reload(true); 1060 } else { 1061 mBrowserFrame.goBackOrForward(msg.arg1); 1062 } 1063 break; 1064 1065 case RESTORE_STATE: 1066 stopLoading(); 1067 restoreState(msg.arg1); 1068 break; 1069 1070 case PAUSE_TIMERS: 1071 mSavedPriority = Process.getThreadPriority(mTid); 1072 Process.setThreadPriority(mTid, 1073 Process.THREAD_PRIORITY_BACKGROUND); 1074 pauseTimers(); 1075 if (CacheManager.disableTransaction()) { 1076 WebCoreThread.mCacheTickersBlocked = true; 1077 sWebCoreHandler.removeMessages( 1078 WebCoreThread.CACHE_TICKER); 1079 } 1080 break; 1081 1082 case RESUME_TIMERS: 1083 Process.setThreadPriority(mTid, mSavedPriority); 1084 resumeTimers(); 1085 if (CacheManager.enableTransaction()) { 1086 WebCoreThread.mCacheTickersBlocked = false; 1087 sWebCoreHandler.sendMessageDelayed( 1088 sWebCoreHandler.obtainMessage( 1089 WebCoreThread.CACHE_TICKER), 1090 WebCoreThread.CACHE_TICKER_INTERVAL); 1091 } 1092 break; 1093 1094 case ON_PAUSE: 1095 nativePause(); 1096 break; 1097 1098 case ON_RESUME: 1099 nativeResume(); 1100 break; 1101 1102 case FREE_MEMORY: 1103 clearCache(false); 1104 nativeFreeMemory(); 1105 break; 1106 1107 case SET_NETWORK_STATE: 1108 if (BrowserFrame.sJavaBridge == null) { 1109 throw new IllegalStateException("No WebView " + 1110 "has been created in this process!"); 1111 } 1112 BrowserFrame.sJavaBridge 1113 .setNetworkOnLine(msg.arg1 == 1); 1114 break; 1115 1116 case CLEAR_CACHE: 1117 clearCache(msg.arg1 == 1); 1118 break; 1119 1120 case CLEAR_HISTORY: 1121 mCallbackProxy.getBackForwardList(). 1122 close(mBrowserFrame.mNativeFrame); 1123 break; 1124 1125 case REPLACE_TEXT: 1126 ReplaceTextData rep = (ReplaceTextData) msg.obj; 1127 nativeReplaceTextfieldText(msg.arg1, msg.arg2, 1128 rep.mReplace, rep.mNewStart, rep.mNewEnd, 1129 rep.mTextGeneration); 1130 break; 1131 1132 case PASS_TO_JS: { 1133 JSKeyData jsData = (JSKeyData) msg.obj; 1134 KeyEvent evt = jsData.mEvent; 1135 int keyCode = evt.getKeyCode(); 1136 int keyValue = evt.getUnicodeChar(); 1137 int generation = msg.arg1; 1138 passToJs(generation, 1139 jsData.mCurrentText, 1140 keyCode, 1141 keyValue, 1142 evt.isDown(), 1143 evt.isShiftPressed(), evt.isAltPressed(), 1144 evt.isSymPressed()); 1145 break; 1146 } 1147 1148 case SAVE_DOCUMENT_STATE: { 1149 CursorData cDat = (CursorData) msg.obj; 1150 nativeSaveDocumentState(cDat.mFrame); 1151 break; 1152 } 1153 1154 case CLEAR_SSL_PREF_TABLE: 1155 Network.getInstance(mContext) 1156 .clearUserSslPrefTable(); 1157 break; 1158 1159 case TOUCH_UP: 1160 TouchUpData touchUpData = (TouchUpData) msg.obj; 1161 nativeTouchUp(touchUpData.mMoveGeneration, 1162 touchUpData.mFrame, touchUpData.mNode, 1163 touchUpData.mX, touchUpData.mY); 1164 break; 1165 1166 case TOUCH_EVENT: { 1167 TouchEventData ted = (TouchEventData) msg.obj; 1168 Message.obtain( 1169 mWebView.mPrivateHandler, 1170 WebView.PREVENT_TOUCH_ID, ted.mAction, 1171 nativeHandleTouchEvent(ted.mAction, ted.mX, 1172 ted.mY, ted.mEventTime)).sendToTarget(); 1173 break; 1174 } 1175 1176 case SET_ACTIVE: 1177 nativeSetFocusControllerActive(msg.arg1 == 1); 1178 break; 1179 1180 case ADD_JS_INTERFACE: 1181 JSInterfaceData jsData = (JSInterfaceData) msg.obj; 1182 mBrowserFrame.addJavascriptInterface(jsData.mObject, 1183 jsData.mInterfaceName); 1184 break; 1185 1186 case REQUEST_EXT_REPRESENTATION: 1187 mBrowserFrame.externalRepresentation( 1188 (Message) msg.obj); 1189 break; 1190 1191 case REQUEST_DOC_AS_TEXT: 1192 mBrowserFrame.documentAsText((Message) msg.obj); 1193 break; 1194 1195 case SET_MOVE_FOCUS: 1196 CursorData focusData = (CursorData) msg.obj; 1197 nativeMoveFocus(focusData.mFrame, focusData.mNode); 1198 break; 1199 1200 case SET_MOVE_MOUSE: 1201 CursorData cursorData = (CursorData) msg.obj; 1202 nativeMoveMouse(cursorData.mFrame, 1203 cursorData.mX, cursorData.mY); 1204 break; 1205 1206 case SET_MOVE_MOUSE_IF_LATEST: 1207 CursorData cData = (CursorData) msg.obj; 1208 nativeMoveMouseIfLatest(cData.mMoveGeneration, 1209 cData.mFrame, 1210 cData.mX, cData.mY); 1211 break; 1212 1213 case REQUEST_CURSOR_HREF: { 1214 Message hrefMsg = (Message) msg.obj; 1215 hrefMsg.getData().putString("url", 1216 nativeRetrieveHref(msg.arg1, msg.arg2)); 1217 hrefMsg.getData().putString("title", 1218 nativeRetrieveAnchorText(msg.arg1, msg.arg2)); 1219 hrefMsg.sendToTarget(); 1220 break; 1221 } 1222 1223 case UPDATE_CACHE_AND_TEXT_ENTRY: 1224 nativeUpdateFrameCache(); 1225 // FIXME: this should provide a minimal rectangle 1226 if (mWebView != null) { 1227 mWebView.postInvalidate(); 1228 } 1229 sendUpdateTextEntry(); 1230 break; 1231 1232 case DOC_HAS_IMAGES: 1233 Message imageResult = (Message) msg.obj; 1234 imageResult.arg1 = 1235 mBrowserFrame.documentHasImages() ? 1 : 0; 1236 imageResult.sendToTarget(); 1237 break; 1238 1239 case DELETE_SELECTION: 1240 TextSelectionData deleteSelectionData 1241 = (TextSelectionData) msg.obj; 1242 nativeDeleteSelection(deleteSelectionData.mStart, 1243 deleteSelectionData.mEnd, msg.arg1); 1244 break; 1245 1246 case SET_SELECTION: 1247 nativeSetSelection(msg.arg1, msg.arg2); 1248 break; 1249 1250 case LISTBOX_CHOICES: 1251 SparseBooleanArray choices = (SparseBooleanArray) 1252 msg.obj; 1253 int choicesSize = msg.arg1; 1254 boolean[] choicesArray = new boolean[choicesSize]; 1255 for (int c = 0; c < choicesSize; c++) { 1256 choicesArray[c] = choices.get(c); 1257 } 1258 nativeSendListBoxChoices(choicesArray, 1259 choicesSize); 1260 break; 1261 1262 case SINGLE_LISTBOX_CHOICE: 1263 nativeSendListBoxChoice(msg.arg1); 1264 break; 1265 1266 case SET_BACKGROUND_COLOR: 1267 nativeSetBackgroundColor(msg.arg1); 1268 break; 1269 1270 case GET_SELECTION: 1271 String str = nativeGetSelection((Region) msg.obj); 1272 Message.obtain(mWebView.mPrivateHandler 1273 , WebView.UPDATE_CLIPBOARD, str) 1274 .sendToTarget(); 1275 break; 1276 1277 case DUMP_DOMTREE: 1278 nativeDumpDomTree(msg.arg1 == 1); 1279 break; 1280 1281 case DUMP_RENDERTREE: 1282 nativeDumpRenderTree(msg.arg1 == 1); 1283 break; 1284 1285 case DUMP_NAVTREE: 1286 nativeDumpNavTree(); 1287 break; 1288 1289 case SET_JS_FLAGS: 1290 nativeSetJsFlags((String)msg.obj); 1291 break; 1292 1293 case GEOLOCATION_PERMISSIONS_PROVIDE: 1294 GeolocationPermissionsData data = 1295 (GeolocationPermissionsData) msg.obj; 1296 nativeGeolocationPermissionsProvide(data.mOrigin, 1297 data.mAllow, data.mRemember); 1298 break; 1299 1300 case SYNC_SCROLL: 1301 mWebkitScrollX = msg.arg1; 1302 mWebkitScrollY = msg.arg2; 1303 break; 1304 1305 case SPLIT_PICTURE_SET: 1306 nativeSplitContent(); 1307 mSplitPictureIsScheduled = false; 1308 break; 1309 1310 case CLEAR_CONTENT: 1311 // Clear the view so that onDraw() will draw nothing 1312 // but white background 1313 // (See public method WebView.clearView) 1314 nativeClearContent(); 1315 break; 1316 1317 case MESSAGE_RELAY: 1318 if (msg.obj instanceof Message) { 1319 ((Message) msg.obj).sendToTarget(); 1320 } 1321 break; 1322 1323 case POPULATE_VISITED_LINKS: 1324 nativeProvideVisitedHistory((String[])msg.obj); 1325 break; 1326 1327 case VALID_NODE_BOUNDS: { 1328 MotionUpData motionUpData = (MotionUpData) msg.obj; 1329 boolean result = nativeValidNodeAndBounds( 1330 motionUpData.mFrame, motionUpData.mNode, 1331 motionUpData.mBounds); 1332 Message message = mWebView.mPrivateHandler 1333 .obtainMessage(WebView.DO_MOTION_UP, 1334 motionUpData.mX, motionUpData.mY, 1335 new Boolean(result)); 1336 mWebView.mPrivateHandler.sendMessageAtFrontOfQueue( 1337 message); 1338 break; 1339 } 1340 1341 case HIDE_FULLSCREEN: 1342 nativeFullScreenPluginHidden(msg.arg1); 1343 break; 1344 } 1345 } 1346 }; 1347 // Take all queued messages and resend them to the new handler. 1348 synchronized (this) { 1349 int size = mMessages.size(); 1350 for (int i = 0; i < size; i++) { 1351 mHandler.sendMessage(mMessages.get(i)); 1352 } 1353 mMessages = null; 1354 } 1355 } 1356 1357 /** 1358 * Send a message internally to the queue or to the handler 1359 */ 1360 private synchronized void sendMessage(Message msg) { 1361 if (mBlockMessages) { 1362 return; 1363 } 1364 if (mMessages != null) { 1365 mMessages.add(msg); 1366 } else { 1367 mHandler.sendMessage(msg); 1368 } 1369 } 1370 1371 private synchronized void removeMessages(int what) { 1372 if (mBlockMessages) { 1373 return; 1374 } 1375 if (what == EventHub.WEBKIT_DRAW) { 1376 mDrawIsScheduled = false; 1377 } 1378 if (mMessages != null) { 1379 Log.w(LOGTAG, "Not supported in this case."); 1380 } else { 1381 mHandler.removeMessages(what); 1382 } 1383 } 1384 1385 private synchronized boolean hasMessages(int what) { 1386 if (mBlockMessages) { 1387 return false; 1388 } 1389 if (mMessages != null) { 1390 Log.w(LOGTAG, "hasMessages() is not supported in this case."); 1391 return false; 1392 } else { 1393 return mHandler.hasMessages(what); 1394 } 1395 } 1396 1397 private synchronized void sendMessageDelayed(Message msg, long delay) { 1398 if (mBlockMessages) { 1399 return; 1400 } 1401 mHandler.sendMessageDelayed(msg, delay); 1402 } 1403 1404 /** 1405 * Send a message internally to the front of the queue. 1406 */ 1407 private synchronized void sendMessageAtFrontOfQueue(Message msg) { 1408 if (mBlockMessages) { 1409 return; 1410 } 1411 if (mMessages != null) { 1412 mMessages.add(0, msg); 1413 } else { 1414 mHandler.sendMessageAtFrontOfQueue(msg); 1415 } 1416 } 1417 1418 /** 1419 * Remove all the messages. 1420 */ 1421 private synchronized void removeMessages() { 1422 // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed 1423 mDrawIsScheduled = false; 1424 mSplitPictureIsScheduled = false; 1425 if (mMessages != null) { 1426 mMessages.clear(); 1427 } else { 1428 mHandler.removeCallbacksAndMessages(null); 1429 } 1430 } 1431 1432 /** 1433 * Block sending messages to the EventHub. 1434 */ 1435 private synchronized void blockMessages() { 1436 mBlockMessages = true; 1437 } 1438 } 1439 1440 //------------------------------------------------------------------------- 1441 // Methods called by host activity (in the same thread) 1442 //------------------------------------------------------------------------- 1443 1444 void stopLoading() { 1445 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading"); 1446 if (mBrowserFrame != null) { 1447 mBrowserFrame.stopLoading(); 1448 } 1449 } 1450 1451 //------------------------------------------------------------------------- 1452 // Methods called by WebView 1453 // If it refers to local variable, it needs synchronized(). 1454 // If it needs WebCore, it has to send message. 1455 //------------------------------------------------------------------------- 1456 1457 void sendMessage(Message msg) { 1458 mEventHub.sendMessage(msg); 1459 } 1460 1461 void sendMessage(int what) { 1462 mEventHub.sendMessage(Message.obtain(null, what)); 1463 } 1464 1465 void sendMessage(int what, Object obj) { 1466 mEventHub.sendMessage(Message.obtain(null, what, obj)); 1467 } 1468 1469 void sendMessage(int what, int arg1) { 1470 // just ignore the second argument (make it 0) 1471 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0)); 1472 } 1473 1474 void sendMessage(int what, int arg1, int arg2) { 1475 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2)); 1476 } 1477 1478 void sendMessage(int what, int arg1, Object obj) { 1479 // just ignore the second argument (make it 0) 1480 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj)); 1481 } 1482 1483 void sendMessage(int what, int arg1, int arg2, Object obj) { 1484 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj)); 1485 } 1486 1487 void sendMessageAtFrontOfQueue(int what, Object obj) { 1488 mEventHub.sendMessageAtFrontOfQueue(Message.obtain( 1489 null, what, obj)); 1490 } 1491 1492 void sendMessageDelayed(int what, Object obj, long delay) { 1493 mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay); 1494 } 1495 1496 void removeMessages(int what) { 1497 mEventHub.removeMessages(what); 1498 } 1499 1500 void removeMessages() { 1501 mEventHub.removeMessages(); 1502 } 1503 1504 /** 1505 * Removes pending messages and trigger a DESTROY message to send to 1506 * WebCore. 1507 * Called from UI thread. 1508 */ 1509 void destroy() { 1510 // We don't want anyone to post a message between removing pending 1511 // messages and sending the destroy message. 1512 synchronized (mEventHub) { 1513 // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to 1514 // be preserved even the WebView is destroyed. 1515 // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS 1516 boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS); 1517 boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS); 1518 mEventHub.removeMessages(); 1519 mEventHub.sendMessageAtFrontOfQueue( 1520 Message.obtain(null, EventHub.DESTROY)); 1521 if (hasPause) { 1522 mEventHub.sendMessageAtFrontOfQueue( 1523 Message.obtain(null, EventHub.PAUSE_TIMERS)); 1524 } 1525 if (hasResume) { 1526 mEventHub.sendMessageAtFrontOfQueue( 1527 Message.obtain(null, EventHub.RESUME_TIMERS)); 1528 } 1529 mEventHub.blockMessages(); 1530 } 1531 } 1532 1533 //------------------------------------------------------------------------- 1534 // WebViewCore private methods 1535 //------------------------------------------------------------------------- 1536 1537 private void clearCache(boolean includeDiskFiles) { 1538 mBrowserFrame.clearCache(); 1539 if (includeDiskFiles) { 1540 CacheManager.removeAllCacheFiles(); 1541 } 1542 } 1543 1544 private void loadUrl(String url) { 1545 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url); 1546 mBrowserFrame.loadUrl(url); 1547 } 1548 1549 private void key(KeyEvent evt, boolean isDown) { 1550 if (DebugFlags.WEB_VIEW_CORE) { 1551 Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", " 1552 + evt); 1553 } 1554 int keyCode = evt.getKeyCode(); 1555 if (!nativeKey(keyCode, evt.getUnicodeChar(), 1556 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(), 1557 evt.isSymPressed(), 1558 isDown) && keyCode != KeyEvent.KEYCODE_ENTER) { 1559 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP 1560 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { 1561 if (DebugFlags.WEB_VIEW_CORE) { 1562 Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode); 1563 } 1564 if (mWebView != null && evt.isDown()) { 1565 Message.obtain(mWebView.mPrivateHandler, 1566 WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget(); 1567 } 1568 return; 1569 } 1570 // bubble up the event handling 1571 // but do not bubble up the ENTER key, which would open the search 1572 // bar without any text. 1573 mCallbackProxy.onUnhandledKeyEvent(evt); 1574 } 1575 } 1576 1577 // These values are used to avoid requesting a layout based on old values 1578 private int mCurrentViewWidth = 0; 1579 private int mCurrentViewHeight = 0; 1580 private float mCurrentViewScale = 1.0f; 1581 1582 // notify webkit that our virtual view size changed size (after inv-zoom) 1583 private void viewSizeChanged(int w, int h, int textwrapWidth, float scale, 1584 boolean ignoreHeight) { 1585 if (DebugFlags.WEB_VIEW_CORE) { 1586 Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h 1587 + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale); 1588 } 1589 if (w == 0) { 1590 Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); 1591 return; 1592 } 1593 int width = w; 1594 if (mSettings.getUseWideViewPort()) { 1595 if (mViewportWidth == -1) { 1596 if (mSettings.getLayoutAlgorithm() == 1597 WebSettings.LayoutAlgorithm.NORMAL) { 1598 width = WebView.DEFAULT_VIEWPORT_WIDTH; 1599 } else { 1600 /* 1601 * if a page's minimum preferred width is wider than the 1602 * given "w", use it instead to get better layout result. If 1603 * we start a page with MAX_ZOOM_WIDTH, "w" will be always 1604 * wider. If we start a page with screen width, due to the 1605 * delay between {@link #didFirstLayout} and 1606 * {@link #viewSizeChanged}, 1607 * {@link #nativeGetContentMinPrefWidth} will return a more 1608 * accurate value than initial 0 to result a better layout. 1609 * In the worse case, the native width will be adjusted when 1610 * next zoom or screen orientation change happens. 1611 */ 1612 width = Math.min(WebView.sMaxViewportWidth, Math.max(w, 1613 Math.max(WebView.DEFAULT_VIEWPORT_WIDTH, 1614 nativeGetContentMinPrefWidth()))); 1615 } 1616 } else { 1617 width = Math.max(w, mViewportWidth); 1618 } 1619 } 1620 nativeSetSize(width, width == w ? h : Math.round((float) width * h / w), 1621 textwrapWidth, scale, w, h, ignoreHeight); 1622 // Remember the current width and height 1623 boolean needInvalidate = (mCurrentViewWidth == 0); 1624 mCurrentViewWidth = w; 1625 mCurrentViewHeight = h; 1626 mCurrentViewScale = scale; 1627 if (needInvalidate) { 1628 // ensure {@link #webkitDraw} is called as we were blocking in 1629 // {@link #contentDraw} when mCurrentViewWidth is 0 1630 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged"); 1631 contentDraw(); 1632 } 1633 mEventHub.sendMessage(Message.obtain(null, 1634 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY)); 1635 } 1636 1637 private void sendUpdateTextEntry() { 1638 if (mWebView != null) { 1639 Message.obtain(mWebView.mPrivateHandler, 1640 WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget(); 1641 } 1642 } 1643 1644 // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize 1645 // callbacks. Computes the sum of database quota for all origins. 1646 private long getUsedQuota() { 1647 WebStorage webStorage = WebStorage.getInstance(); 1648 Collection<WebStorage.Origin> origins = webStorage.getOriginsSync(); 1649 1650 if (origins == null) { 1651 return 0; 1652 } 1653 long usedQuota = 0; 1654 for (WebStorage.Origin website : origins) { 1655 usedQuota += website.getQuota(); 1656 } 1657 return usedQuota; 1658 } 1659 1660 // Used to avoid posting more than one draw message. 1661 private boolean mDrawIsScheduled; 1662 1663 // Used to avoid posting more than one split picture message. 1664 private boolean mSplitPictureIsScheduled; 1665 1666 // mRestoreState is set in didFirstLayout(), and reset in the next 1667 // webkitDraw after passing it to the UI thread. 1668 private RestoreState mRestoreState = null; 1669 1670 static class RestoreState { 1671 float mMinScale; 1672 float mMaxScale; 1673 float mViewScale; 1674 float mTextWrapScale; 1675 float mDefaultScale; 1676 int mScrollX; 1677 int mScrollY; 1678 boolean mMobileSite; 1679 } 1680 1681 static class DrawData { 1682 DrawData() { 1683 mInvalRegion = new Region(); 1684 mWidthHeight = new Point(); 1685 } 1686 Region mInvalRegion; 1687 Point mViewPoint; 1688 Point mWidthHeight; 1689 int mMinPrefWidth; 1690 RestoreState mRestoreState; // only non-null if it is for the first 1691 // picture set after the first layout 1692 boolean mFocusSizeChanged; 1693 } 1694 1695 private void webkitDraw() { 1696 mDrawIsScheduled = false; 1697 DrawData draw = new DrawData(); 1698 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); 1699 if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight) 1700 == false) { 1701 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort"); 1702 return; 1703 } 1704 if (mWebView != null) { 1705 // Send the native view size that was used during the most recent 1706 // layout. 1707 draw.mFocusSizeChanged = nativeFocusBoundsChanged(); 1708 draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight); 1709 if (mSettings.getUseWideViewPort()) { 1710 draw.mMinPrefWidth = Math.max( 1711 mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH 1712 : (mViewportWidth == 0 ? mCurrentViewWidth 1713 : mViewportWidth), 1714 nativeGetContentMinPrefWidth()); 1715 } 1716 if (mRestoreState != null) { 1717 draw.mRestoreState = mRestoreState; 1718 mRestoreState = null; 1719 } 1720 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); 1721 Message.obtain(mWebView.mPrivateHandler, 1722 WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); 1723 if (mWebkitScrollX != 0 || mWebkitScrollY != 0) { 1724 // as we have the new picture, try to sync the scroll position 1725 Message.obtain(mWebView.mPrivateHandler, 1726 WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX, 1727 mWebkitScrollY).sendToTarget(); 1728 mWebkitScrollX = mWebkitScrollY = 0; 1729 } 1730 } 1731 } 1732 1733 /////////////////////////////////////////////////////////////////////////// 1734 // These are called from the UI thread, not our thread 1735 1736 static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG | 1737 Paint.DITHER_FLAG | 1738 Paint.SUBPIXEL_TEXT_FLAG; 1739 static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG | 1740 Paint.DITHER_FLAG; 1741 1742 final DrawFilter mZoomFilter = 1743 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG); 1744 final DrawFilter mScrollFilter = null; 1745 // If we need to trade more speed for less quality on slower devices 1746 // use this: new PaintFlagsDrawFilter(SCROLL_BITS, 0); 1747 1748 /* package */ void drawContentPicture(Canvas canvas, int color, 1749 boolean animatingZoom, 1750 boolean animatingScroll) { 1751 DrawFilter df = null; 1752 if (animatingZoom) { 1753 df = mZoomFilter; 1754 } else if (animatingScroll) { 1755 df = mScrollFilter; 1756 } 1757 canvas.setDrawFilter(df); 1758 boolean tookTooLong = nativeDrawContent(canvas, color); 1759 canvas.setDrawFilter(null); 1760 if (tookTooLong && mSplitPictureIsScheduled == false) { 1761 mSplitPictureIsScheduled = true; 1762 sendMessage(EventHub.SPLIT_PICTURE_SET); 1763 } 1764 } 1765 1766 /* package */ synchronized boolean pictureReady() { 1767 return 0 != mNativeClass ? nativePictureReady() : false; 1768 } 1769 1770 /*package*/ synchronized Picture copyContentPicture() { 1771 Picture result = new Picture(); 1772 if (0 != mNativeClass) { 1773 nativeCopyContentToPicture(result); 1774 } 1775 return result; 1776 } 1777 1778 static void pauseUpdate(WebViewCore core) { 1779 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1780 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1781 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1782 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1783 .obtainMessage(WebCoreThread.REDUCE_PRIORITY)); 1784 } 1785 1786 static void resumeUpdate(WebViewCore core) { 1787 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1788 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1789 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1790 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1791 .obtainMessage(WebCoreThread.RESUME_PRIORITY)); 1792 } 1793 1794 static void startCacheTransaction() { 1795 sWebCoreHandler.sendMessage(sWebCoreHandler 1796 .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER)); 1797 } 1798 1799 static void endCacheTransaction() { 1800 sWebCoreHandler.sendMessage(sWebCoreHandler 1801 .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER)); 1802 } 1803 1804 ////////////////////////////////////////////////////////////////////////// 1805 1806 private void restoreState(int index) { 1807 WebBackForwardList list = mCallbackProxy.getBackForwardList(); 1808 int size = list.getSize(); 1809 for (int i = 0; i < size; i++) { 1810 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); 1811 } 1812 mBrowserFrame.mLoadInitFromJava = true; 1813 list.restoreIndex(mBrowserFrame.mNativeFrame, index); 1814 mBrowserFrame.mLoadInitFromJava = false; 1815 } 1816 1817 //------------------------------------------------------------------------- 1818 // Implement abstract methods in WebViewCore, native WebKit callback part 1819 //------------------------------------------------------------------------- 1820 1821 // called from JNI or WebView thread 1822 /* package */ void contentDraw() { 1823 // don't update the Picture until we have an initial width and finish 1824 // the first layout 1825 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { 1826 return; 1827 } 1828 // only fire an event if this is our first request 1829 synchronized (this) { 1830 if (mDrawIsScheduled) return; 1831 mDrawIsScheduled = true; 1832 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 1833 } 1834 } 1835 1836 // called by JNI 1837 private void contentScrollBy(int dx, int dy, boolean animate) { 1838 if (!mBrowserFrame.firstLayoutDone()) { 1839 // Will this happen? If yes, we need to do something here. 1840 return; 1841 } 1842 if (mWebView != null) { 1843 Message msg = Message.obtain(mWebView.mPrivateHandler, 1844 WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate)); 1845 if (mDrawIsScheduled) { 1846 mEventHub.sendMessage(Message.obtain(null, 1847 EventHub.MESSAGE_RELAY, msg)); 1848 } else { 1849 msg.sendToTarget(); 1850 } 1851 } 1852 } 1853 1854 // called by JNI 1855 private void contentScrollTo(int x, int y) { 1856 if (!mBrowserFrame.firstLayoutDone()) { 1857 /* 1858 * WebKit restore state will be called before didFirstLayout(), 1859 * remember the position as it has to be applied after restoring 1860 * zoom factor which is controlled by screenWidth. 1861 */ 1862 mRestoredX = x; 1863 mRestoredY = y; 1864 return; 1865 } 1866 if (mWebView != null) { 1867 Message msg = Message.obtain(mWebView.mPrivateHandler, 1868 WebView.SCROLL_TO_MSG_ID, x, y); 1869 if (mDrawIsScheduled) { 1870 mEventHub.sendMessage(Message.obtain(null, 1871 EventHub.MESSAGE_RELAY, msg)); 1872 } else { 1873 msg.sendToTarget(); 1874 } 1875 } 1876 } 1877 1878 // called by JNI 1879 private void contentSpawnScrollTo(int x, int y) { 1880 if (!mBrowserFrame.firstLayoutDone()) { 1881 /* 1882 * WebKit restore state will be called before didFirstLayout(), 1883 * remember the position as it has to be applied after restoring 1884 * zoom factor which is controlled by screenWidth. 1885 */ 1886 mRestoredX = x; 1887 mRestoredY = y; 1888 return; 1889 } 1890 if (mWebView != null) { 1891 Message msg = Message.obtain(mWebView.mPrivateHandler, 1892 WebView.SPAWN_SCROLL_TO_MSG_ID, x, y); 1893 if (mDrawIsScheduled) { 1894 mEventHub.sendMessage(Message.obtain(null, 1895 EventHub.MESSAGE_RELAY, msg)); 1896 } else { 1897 msg.sendToTarget(); 1898 } 1899 } 1900 } 1901 1902 // called by JNI 1903 private void sendNotifyProgressFinished() { 1904 sendUpdateTextEntry(); 1905 // as CacheManager can behave based on database transaction, we need to 1906 // call tick() to trigger endTransaction 1907 sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER); 1908 sWebCoreHandler.sendMessage(sWebCoreHandler 1909 .obtainMessage(WebCoreThread.CACHE_TICKER)); 1910 contentDraw(); 1911 } 1912 1913 /* Called by JNI. The coordinates are in doc coordinates, so they need to 1914 be scaled before they can be used by the view system, which happens 1915 in WebView since it (and its thread) know the current scale factor. 1916 */ 1917 private void sendViewInvalidate(int left, int top, int right, int bottom) { 1918 if (mWebView != null) { 1919 Message.obtain(mWebView.mPrivateHandler, 1920 WebView.INVAL_RECT_MSG_ID, 1921 new Rect(left, top, right, bottom)).sendToTarget(); 1922 } 1923 } 1924 1925 private static boolean mRepaintScheduled = false; 1926 1927 /* 1928 * Called by the WebView thread 1929 */ 1930 /* package */ void signalRepaintDone() { 1931 mRepaintScheduled = false; 1932 } 1933 1934 // called by JNI 1935 private void sendImmediateRepaint() { 1936 if (mWebView != null && !mRepaintScheduled) { 1937 mRepaintScheduled = true; 1938 Message.obtain(mWebView.mPrivateHandler, 1939 WebView.IMMEDIATE_REPAINT_MSG_ID).sendToTarget(); 1940 } 1941 } 1942 1943 // called by JNI 1944 private void setRootLayer(int layer) { 1945 if (mWebView != null) { 1946 Message.obtain(mWebView.mPrivateHandler, 1947 WebView.SET_ROOT_LAYER_MSG_ID, 1948 layer, 0).sendToTarget(); 1949 } 1950 } 1951 1952 /* package */ WebView getWebView() { 1953 return mWebView; 1954 } 1955 1956 private native void setViewportSettingsFromNative(); 1957 1958 // called by JNI 1959 private void didFirstLayout(boolean standardLoad) { 1960 if (DebugFlags.WEB_VIEW_CORE) { 1961 Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad); 1962 } 1963 1964 mBrowserFrame.didFirstLayout(); 1965 1966 if (mWebView == null) return; 1967 1968 boolean updateRestoreState = standardLoad || mRestoredScale > 0; 1969 setupViewport(updateRestoreState); 1970 // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will 1971 // be called after the WebView restore the state. If updateRestoreState 1972 // is false, start to draw now as it is ready. 1973 if (!updateRestoreState) { 1974 mWebView.mViewManager.postReadyToDrawAll(); 1975 } 1976 1977 // reset the scroll position, the restored offset and scales 1978 mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY 1979 = mRestoredScale = mRestoredScreenWidthScale = 0; 1980 } 1981 1982 // called by JNI 1983 private void updateViewport() { 1984 // if updateViewport is called before first layout, wait until first 1985 // layout to update the viewport. In the rare case, this is called after 1986 // first layout, force an update as we have just parsed the viewport 1987 // meta tag. 1988 if (mBrowserFrame.firstLayoutDone()) { 1989 setupViewport(true); 1990 } 1991 } 1992 1993 private void setupViewport(boolean updateRestoreState) { 1994 // set the viewport settings from WebKit 1995 setViewportSettingsFromNative(); 1996 1997 // adjust the default scale to match the densityDpi 1998 float adjust = 1.0f; 1999 if (mViewportDensityDpi == -1) { 2000 if (WebView.DEFAULT_SCALE_PERCENT != 100) { 2001 adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f; 2002 } 2003 } else if (mViewportDensityDpi > 0) { 2004 adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi 2005 / mViewportDensityDpi; 2006 } 2007 int defaultScale = (int) (adjust * 100); 2008 2009 if (mViewportInitialScale > 0) { 2010 mViewportInitialScale *= adjust; 2011 } 2012 if (mViewportMinimumScale > 0) { 2013 mViewportMinimumScale *= adjust; 2014 } 2015 if (mViewportMaximumScale > 0) { 2016 mViewportMaximumScale *= adjust; 2017 } 2018 2019 // infer the values if they are not defined. 2020 if (mViewportWidth == 0) { 2021 if (mViewportInitialScale == 0) { 2022 mViewportInitialScale = defaultScale; 2023 } 2024 } 2025 if (mViewportUserScalable == false) { 2026 mViewportInitialScale = defaultScale; 2027 mViewportMinimumScale = defaultScale; 2028 mViewportMaximumScale = defaultScale; 2029 } 2030 if (mViewportMinimumScale > mViewportInitialScale 2031 && mViewportInitialScale != 0) { 2032 mViewportMinimumScale = mViewportInitialScale; 2033 } 2034 if (mViewportMaximumScale > 0 2035 && mViewportMaximumScale < mViewportInitialScale) { 2036 mViewportMaximumScale = mViewportInitialScale; 2037 } 2038 if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) { 2039 mViewportWidth = 0; 2040 } 2041 2042 // if mViewportWidth is 0, it means device-width, always update. 2043 if (mViewportWidth != 0 && !updateRestoreState) return; 2044 2045 // now notify webview 2046 // webViewWidth refers to the width in the view system 2047 int webViewWidth; 2048 // viewportWidth refers to the width in the document system 2049 int viewportWidth = mCurrentViewWidth; 2050 if (viewportWidth == 0) { 2051 // this may happen when WebView just starts. This is not perfect as 2052 // we call WebView method from WebCore thread. But not perfect 2053 // reference is better than no reference. 2054 webViewWidth = mWebView.getViewWidth(); 2055 viewportWidth = (int) (webViewWidth / adjust); 2056 if (viewportWidth == 0) { 2057 Log.w(LOGTAG, "Can't get the viewWidth after the first layout"); 2058 } 2059 } else { 2060 webViewWidth = Math.round(viewportWidth * mCurrentViewScale); 2061 } 2062 mRestoreState = new RestoreState(); 2063 mRestoreState.mMinScale = mViewportMinimumScale / 100.0f; 2064 mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f; 2065 mRestoreState.mDefaultScale = adjust; 2066 mRestoreState.mScrollX = mRestoredX; 2067 mRestoreState.mScrollY = mRestoredY; 2068 mRestoreState.mMobileSite = (0 == mViewportWidth); 2069 if (mRestoredScale > 0) { 2070 if (mRestoredScreenWidthScale > 0) { 2071 mRestoreState.mTextWrapScale = 2072 mRestoredScreenWidthScale / 100.0f; 2073 // 0 will trigger WebView to turn on zoom overview mode 2074 mRestoreState.mViewScale = 0; 2075 } else { 2076 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 2077 mRestoredScale / 100.0f; 2078 } 2079 } else { 2080 if (mViewportInitialScale > 0) { 2081 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 2082 mViewportInitialScale / 100.0f; 2083 } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) { 2084 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 2085 (float) webViewWidth / mViewportWidth; 2086 } else { 2087 mRestoreState.mTextWrapScale = adjust; 2088 // 0 will trigger WebView to turn on zoom overview mode 2089 mRestoreState.mViewScale = 0; 2090 } 2091 } 2092 2093 if (mWebView.mHeightCanMeasure) { 2094 // Trick to ensure that the Picture has the exact height for the 2095 // content by forcing to layout with 0 height after the page is 2096 // ready, which is indicated by didFirstLayout. This is essential to 2097 // get rid of the white space in the GMail which uses WebView for 2098 // message view. 2099 mWebView.mLastHeightSent = 0; 2100 // Send a negative scale to indicate that WebCore should reuse 2101 // the current scale 2102 WebView.ViewSizeData data = new WebView.ViewSizeData(); 2103 data.mWidth = mWebView.mLastWidthSent; 2104 data.mHeight = 0; 2105 // if mHeightCanMeasure is true, getUseWideViewPort() can't be 2106 // true. It is safe to use mWidth for mTextWrapWidth. 2107 data.mTextWrapWidth = data.mWidth; 2108 data.mScale = -1.0f; 2109 data.mIgnoreHeight = false; 2110 // send VIEW_SIZE_CHANGED to the front of the queue so that we can 2111 // avoid pushing the wrong picture to the WebView side. If there is 2112 // a VIEW_SIZE_CHANGED in the queue, probably from WebView side, 2113 // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED 2114 // in the queue, as mLastHeightSent has been updated here, we may 2115 // miss the requestLayout in WebView side after the new picture. 2116 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2117 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 2118 EventHub.VIEW_SIZE_CHANGED, data)); 2119 } else if (mSettings.getUseWideViewPort()) { 2120 if (viewportWidth == 0) { 2121 // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView 2122 // to WebViewCore 2123 mWebView.mLastWidthSent = 0; 2124 } else { 2125 WebView.ViewSizeData data = new WebView.ViewSizeData(); 2126 // mViewScale as 0 means it is in zoom overview mode. So we don't 2127 // know the exact scale. If mRestoredScale is non-zero, use it; 2128 // otherwise just use mTextWrapScale as the initial scale. 2129 data.mScale = mRestoreState.mViewScale == 0 2130 ? (mRestoredScale > 0 ? mRestoredScale / 100.0f 2131 : mRestoreState.mTextWrapScale) 2132 : mRestoreState.mViewScale; 2133 if (DebugFlags.WEB_VIEW_CORE) { 2134 Log.v(LOGTAG, "setupViewport" 2135 + " mRestoredScale=" + mRestoredScale 2136 + " mViewScale=" + mRestoreState.mViewScale 2137 + " mTextWrapScale=" + mRestoreState.mTextWrapScale 2138 ); 2139 } 2140 data.mWidth = Math.round(webViewWidth / data.mScale); 2141 data.mHeight = mCurrentViewHeight * data.mWidth / viewportWidth; 2142 data.mTextWrapWidth = Math.round(webViewWidth 2143 / mRestoreState.mTextWrapScale); 2144 data.mIgnoreHeight = false; 2145 // send VIEW_SIZE_CHANGED to the front of the queue so that we 2146 // can avoid pushing the wrong picture to the WebView side. 2147 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2148 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 2149 EventHub.VIEW_SIZE_CHANGED, data)); 2150 } 2151 } 2152 } 2153 2154 // called by JNI 2155 private void restoreScale(int scale) { 2156 if (mBrowserFrame.firstLayoutDone() == false) { 2157 mRestoredScale = scale; 2158 } 2159 } 2160 2161 // called by JNI 2162 private void restoreScreenWidthScale(int scale) { 2163 if (!mSettings.getUseWideViewPort()) { 2164 return; 2165 } 2166 2167 if (mBrowserFrame.firstLayoutDone() == false) { 2168 mRestoredScreenWidthScale = scale; 2169 } 2170 } 2171 2172 // called by JNI 2173 private void needTouchEvents(boolean need) { 2174 if (mWebView != null) { 2175 Message.obtain(mWebView.mPrivateHandler, 2176 WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0) 2177 .sendToTarget(); 2178 } 2179 } 2180 2181 // called by JNI 2182 private void updateTextfield(int ptr, boolean changeToPassword, 2183 String text, int textGeneration) { 2184 if (mWebView != null) { 2185 Message msg = Message.obtain(mWebView.mPrivateHandler, 2186 WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 2187 textGeneration, text); 2188 msg.getData().putBoolean("password", changeToPassword); 2189 msg.sendToTarget(); 2190 } 2191 } 2192 2193 // called by JNI 2194 private void updateTextSelection(int pointer, int start, int end, 2195 int textGeneration) { 2196 if (mWebView != null) { 2197 Message.obtain(mWebView.mPrivateHandler, 2198 WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration, 2199 new TextSelectionData(start, end)).sendToTarget(); 2200 } 2201 } 2202 2203 // called by JNI 2204 private void clearTextEntry() { 2205 if (mWebView == null) return; 2206 Message.obtain(mWebView.mPrivateHandler, 2207 WebView.CLEAR_TEXT_ENTRY).sendToTarget(); 2208 } 2209 2210 // called by JNI 2211 private void sendFindAgain() { 2212 if (mWebView == null) return; 2213 Message.obtain(mWebView.mPrivateHandler, 2214 WebView.FIND_AGAIN).sendToTarget(); 2215 } 2216 2217 private native void nativeUpdateFrameCacheIfLoading(); 2218 private native String nativeRequestLabel(int framePtr, int nodePtr); 2219 /** 2220 * Scroll the focused textfield to (xPercent, y) in document space 2221 */ 2222 private native void nativeScrollFocusedTextInput(float xPercent, int y); 2223 2224 // these must be in document space (i.e. not scaled/zoomed). 2225 private native void nativeSetScrollOffset(int gen, int dx, int dy); 2226 2227 private native void nativeSetGlobalBounds(int x, int y, int w, int h); 2228 2229 // called by JNI 2230 private void requestListBox(String[] array, int[] enabledArray, 2231 int[] selectedArray) { 2232 if (mWebView != null) { 2233 mWebView.requestListBox(array, enabledArray, selectedArray); 2234 } 2235 } 2236 2237 // called by JNI 2238 private void requestListBox(String[] array, int[] enabledArray, 2239 int selection) { 2240 if (mWebView != null) { 2241 mWebView.requestListBox(array, enabledArray, selection); 2242 } 2243 2244 } 2245 2246 // called by JNI 2247 private void requestKeyboard(boolean showKeyboard, boolean isTextView) { 2248 if (mWebView != null) { 2249 Message.obtain(mWebView.mPrivateHandler, 2250 WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 2251 isTextView ? 1 : 0) 2252 .sendToTarget(); 2253 } 2254 } 2255 2256 // called by JNI 2257 private Context getContext() { 2258 return mContext; 2259 } 2260 2261 // called by JNI 2262 private Class<?> getPluginClass(String libName, String clsName) { 2263 2264 if (mWebView == null) { 2265 return null; 2266 } 2267 2268 PluginManager pluginManager = PluginManager.getInstance(null); 2269 2270 String pkgName = pluginManager.getPluginsAPKName(libName); 2271 if (pkgName == null) { 2272 Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); 2273 return null; 2274 } 2275 2276 try { 2277 return pluginManager.getPluginClass(pkgName, clsName); 2278 } catch (NameNotFoundException e) { 2279 Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")"); 2280 } catch (ClassNotFoundException e) { 2281 Log.e(LOGTAG, "Unable to find plugin class (" + clsName + 2282 ") in the apk (" + pkgName + ")"); 2283 } 2284 2285 return null; 2286 } 2287 2288 // called by JNI. PluginWidget function to launch a full-screen view using a 2289 // View object provided by the plugin class. 2290 private void showFullScreenPlugin(ViewManager.ChildView childView, 2291 final int npp, int x, int y, int width, int height) { 2292 2293 if (mWebView == null) { 2294 return; 2295 } 2296 2297 PluginFullScreenData data = new PluginFullScreenData(); 2298 data.mView = childView.mView; 2299 data.mNpp = npp; 2300 data.mDocX = x; 2301 data.mDocY = y; 2302 data.mDocWidth = width; 2303 data.mDocHeight = height; 2304 mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN, data) 2305 .sendToTarget(); 2306 } 2307 2308 // called by JNI 2309 private void hideFullScreenPlugin() { 2310 if (mWebView == null) { 2311 return; 2312 } 2313 mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN) 2314 .sendToTarget(); 2315 } 2316 2317 // called by JNI 2318 private void updateFullScreenPlugin(int x, int y, int width, int height) { 2319 if (mWebView == null) { 2320 return; 2321 } 2322 2323 PluginFullScreenData data = new PluginFullScreenData(); 2324 data.mDocX = x; 2325 data.mDocY = y; 2326 data.mDocWidth = width; 2327 data.mDocHeight = height; 2328 // null mView and mNpp to indicate it is an update 2329 mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN, data) 2330 .sendToTarget(); 2331 } 2332 2333 // called by JNI. PluginWidget functions for creating an embedded View for 2334 // the surface drawing model. 2335 private ViewManager.ChildView addSurface(View pluginView, int x, int y, 2336 int width, int height) { 2337 if (mWebView == null) { 2338 return null; 2339 } 2340 2341 if (pluginView == null) { 2342 Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy"); 2343 return null; 2344 } 2345 2346 // ensures the view system knows the view can redraw itself 2347 pluginView.setWillNotDraw(false); 2348 2349 if(pluginView instanceof SurfaceView) 2350 ((SurfaceView)pluginView).setZOrderOnTop(true); 2351 2352 ViewManager.ChildView view = mWebView.mViewManager.createView(); 2353 view.mView = pluginView; 2354 view.attachView(x, y, width, height); 2355 return view; 2356 } 2357 2358 private void updateSurface(ViewManager.ChildView childView, int x, int y, 2359 int width, int height) { 2360 childView.attachView(x, y, width, height); 2361 } 2362 2363 private void destroySurface(ViewManager.ChildView childView) { 2364 childView.removeView(); 2365 } 2366 2367 private native void nativePause(); 2368 private native void nativeResume(); 2369 private native void nativeFreeMemory(); 2370 private native void nativeFullScreenPluginHidden(int npp); 2371 private native boolean nativeValidNodeAndBounds(int frame, int node, 2372 Rect bounds); 2373 2374} 2375