WebViewCore.java revision 8a7e177bb546eff9cff0617471e55d012ece6190
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.app.ActivityManager; 20import android.content.Context; 21import android.content.pm.PackageManager.NameNotFoundException; 22import android.database.Cursor; 23import android.graphics.Point; 24import android.graphics.Rect; 25import android.graphics.Region; 26import android.media.MediaFile; 27import android.net.ProxyProperties; 28import android.net.Uri; 29import android.net.http.CertificateChainValidator; 30import android.os.Bundle; 31import android.os.Handler; 32import android.os.Looper; 33import android.os.Message; 34import android.os.Process; 35import android.provider.MediaStore; 36import android.util.Log; 37import android.util.SparseBooleanArray; 38import android.view.KeyEvent; 39import android.view.MotionEvent; 40import android.view.SurfaceView; 41import android.view.View; 42import android.webkit.WebViewClassic.FocusNodeHref; 43import android.webkit.WebViewInputDispatcher.WebKitCallbacks; 44 45import junit.framework.Assert; 46 47import java.util.ArrayList; 48import java.util.Collection; 49import java.util.Map; 50import java.util.Set; 51 52/** 53 * @hide 54 */ 55public final class WebViewCore { 56 57 private static final String LOGTAG = "webcore"; 58 59 static { 60 // Load libwebcore and libchromium_net during static initialization. 61 // This happens in the zygote process so they will be shared read-only 62 // across all app processes. 63 try { 64 System.loadLibrary("webcore"); 65 System.loadLibrary("chromium_net"); 66 } catch (UnsatisfiedLinkError e) { 67 Log.e(LOGTAG, "Unable to load native support libraries."); 68 } 69 } 70 71 /* 72 * WebViewCore always executes in the same thread as the native webkit. 73 */ 74 75 // The WebViewClassic that corresponds to this WebViewCore. 76 private WebViewClassic mWebViewClassic; 77 // Proxy for handling callbacks from native code 78 private final CallbackProxy mCallbackProxy; 79 // Settings object for maintaining all settings 80 private final WebSettingsClassic mSettings; 81 // Context for initializing the BrowserFrame with the proper assets. 82 private final Context mContext; 83 // The pointer to a native view object. 84 private int mNativeClass; 85 // The BrowserFrame is an interface to the native Frame component. 86 private BrowserFrame mBrowserFrame; 87 // Custom JS interfaces to add during the initialization. 88 private Map<String, Object> mJavascriptInterfaces; 89 /* 90 * range is from 200 to 10,000. 0 is a special value means device-width. -1 91 * means undefined. 92 */ 93 private int mViewportWidth = -1; 94 95 /* 96 * range is from 200 to 10,000. 0 is a special value means device-height. -1 97 * means undefined. 98 */ 99 private int mViewportHeight = -1; 100 101 /* 102 * scale in percent, range is from 1 to 1000. 0 means undefined. 103 */ 104 private int mViewportInitialScale = 0; 105 106 /* 107 * scale in percent, range is from 1 to 1000. 0 means undefined. 108 */ 109 private int mViewportMinimumScale = 0; 110 111 /* 112 * scale in percent, range is from 1 to 1000. 0 means undefined. 113 */ 114 private int mViewportMaximumScale = 0; 115 116 private boolean mViewportUserScalable = true; 117 118 /* 119 * range is from 70 to 400. 120 * 0 is a special value means device-dpi. The default scale factor will be 121 * always 100. 122 * -1 means undefined. The default scale factor will be 123 * WebView.DEFAULT_SCALE_PERCENT. 124 */ 125 private int mViewportDensityDpi = -1; 126 127 private boolean mIsRestored = false; 128 private float mRestoredScale = 0; 129 private float mRestoredTextWrapScale = 0; 130 private int mRestoredX = 0; 131 private int mRestoredY = 0; 132 133 private DeviceMotionAndOrientationManager mDeviceMotionAndOrientationManager = 134 new DeviceMotionAndOrientationManager(this); 135 private DeviceMotionService mDeviceMotionService; 136 private DeviceOrientationService mDeviceOrientationService; 137 138 private int mLowMemoryUsageThresholdMb; 139 private int mHighMemoryUsageThresholdMb; 140 private int mHighUsageDeltaMb; 141 142 private int mChromeCanFocusDirection; 143 144 // The thread name used to identify the WebCore thread and for use in 145 // debugging other classes that require operation within the WebCore thread. 146 /* package */ static final String THREAD_NAME = "WebViewCoreThread"; 147 148 public WebViewCore(Context context, WebViewClassic w, CallbackProxy proxy, 149 Map<String, Object> javascriptInterfaces) { 150 // No need to assign this in the WebCore thread. 151 mCallbackProxy = proxy; 152 mWebViewClassic = w; 153 mJavascriptInterfaces = javascriptInterfaces; 154 // This context object is used to initialize the WebViewCore during 155 // subwindow creation. 156 mContext = context; 157 158 // We need to wait for the initial thread creation before sending 159 // a message to the WebCore thread. 160 // XXX: This is the only time the UI thread will wait for the WebCore 161 // thread! 162 synchronized (WebViewCore.class) { 163 if (sWebCoreHandler == null) { 164 // Create a global thread and start it. 165 Thread t = new Thread(new WebCoreThread()); 166 t.setName(THREAD_NAME); 167 t.start(); 168 try { 169 WebViewCore.class.wait(); 170 } catch (InterruptedException e) { 171 Log.e(LOGTAG, "Caught exception while waiting for thread " + 172 "creation."); 173 Log.e(LOGTAG, Log.getStackTraceString(e)); 174 } 175 176 // Start the singleton watchdog which will monitor the WebCore thread 177 // to verify it's still processing messages. 178 WebCoreThreadWatchdog.start(context, sWebCoreHandler); 179 } 180 } 181 // Create an EventHub to handle messages before and after the thread is 182 // ready. 183 mEventHub = new EventHub(); 184 // Create a WebSettings object for maintaining all settings 185 mSettings = new WebSettingsClassic(mContext, mWebViewClassic); 186 // The WebIconDatabase needs to be initialized within the UI thread so 187 // just request the instance here. 188 WebIconDatabase.getInstance(); 189 // Create the WebStorage singleton and the UI handler 190 WebStorage.getInstance().createUIHandler(); 191 // Create the UI handler for GeolocationPermissions 192 GeolocationPermissions.getInstance().createUIHandler(); 193 194 // Get the memory class of the current device. V8 will use these values 195 // to GC more effectively. 196 ActivityManager manager = (ActivityManager) mContext.getSystemService( 197 Context.ACTIVITY_SERVICE); 198 ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); 199 manager.getMemoryInfo(memInfo); 200 201 // Allow us to use up to our memory class value before V8's GC kicks in. 202 // These values have been determined by experimentation. 203 mLowMemoryUsageThresholdMb = manager.getLargeMemoryClass(); 204 mHighMemoryUsageThresholdMb = (int) (mLowMemoryUsageThresholdMb * 1.5); 205 // Avoid constant V8 GC when memory usage equals to working set estimate. 206 mHighUsageDeltaMb = mLowMemoryUsageThresholdMb / 32; 207 208 // Send a message to initialize the WebViewCore. 209 Message init = sWebCoreHandler.obtainMessage( 210 WebCoreThread.INITIALIZE, this); 211 sWebCoreHandler.sendMessage(init); 212 } 213 214 /* Initialize private data within the WebCore thread. 215 */ 216 private void initialize() { 217 /* Initialize our private BrowserFrame class to handle all 218 * frame-related functions. We need to create a new view which 219 * in turn creates a C level FrameView and attaches it to the frame. 220 */ 221 mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy, 222 mSettings, mJavascriptInterfaces); 223 mJavascriptInterfaces = null; 224 // Sync the native settings and also create the WebCore thread handler. 225 mSettings.syncSettingsAndCreateHandler(mBrowserFrame); 226 // Create the handler and transfer messages for the IconDatabase 227 WebIconDatabase.getInstance().createHandler(); 228 // Create the handler for WebStorage 229 WebStorage.getInstance().createHandler(); 230 // Create the handler for GeolocationPermissions. 231 GeolocationPermissions.getInstance().createHandler(); 232 // The transferMessages call will transfer all pending messages to the 233 // WebCore thread handler. 234 mEventHub.transferMessages(); 235 236 // Send a message back to WebView to tell it that we have set up the 237 // WebCore thread. 238 if (mWebViewClassic != null) { 239 Message.obtain(mWebViewClassic.mPrivateHandler, 240 WebViewClassic.WEBCORE_INITIALIZED_MSG_ID, 241 mNativeClass, 0).sendToTarget(); 242 } 243 244 } 245 246 /* Handle the initialization of WebViewCore during subwindow creation. This 247 * method is called from the WebCore thread but it is called before the 248 * INITIALIZE message can be handled. 249 */ 250 /* package */ void initializeSubwindow() { 251 // Go ahead and initialize the core components. 252 initialize(); 253 // Remove the INITIALIZE method so we don't try to initialize twice. 254 sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this); 255 } 256 257 /* Get the BrowserFrame component. This is used for subwindow creation and 258 * is called only from BrowserFrame in the WebCore thread. */ 259 /* package */ synchronized BrowserFrame getBrowserFrame() { 260 return mBrowserFrame; 261 } 262 263 public WebKitCallbacks getInputDispatcherCallbacks() { 264 return mEventHub; 265 } 266 267 //------------------------------------------------------------------------- 268 // Common methods 269 //------------------------------------------------------------------------- 270 271 /** 272 * Causes all timers to pause. This applies to all WebViews in the current 273 * app process. 274 */ 275 public static void pauseTimers() { 276 if (BrowserFrame.sJavaBridge == null) { 277 throw new IllegalStateException( 278 "No WebView has been created in this process!"); 279 } 280 BrowserFrame.sJavaBridge.pause(); 281 } 282 283 /** 284 * Resume all timers. This applies to all WebViews in the current process. 285 */ 286 public static void resumeTimers() { 287 if (BrowserFrame.sJavaBridge == null) { 288 throw new IllegalStateException( 289 "No WebView has been created in this process!"); 290 } 291 BrowserFrame.sJavaBridge.resume(); 292 } 293 294 public WebSettingsClassic getSettings() { 295 return mSettings; 296 } 297 298 /* 299 * Given mimeType, check whether it's supported in Android media framework. 300 * mimeType could be such as "audio/ogg" and "video/mp4". 301 */ 302 /* package */ static boolean isSupportedMediaMimeType(String mimeType) { 303 int fileType = MediaFile.getFileTypeForMimeType(mimeType); 304 return MediaFile.isAudioFileType(fileType) 305 || MediaFile.isVideoFileType(fileType) 306 || MediaFile.isPlayListFileType(fileType) 307 // The following is not in Media framework, but it's supported. 308 || (mimeType != null && mimeType.startsWith("video/m4v")); 309 } 310 311 /** 312 * Add an error message to the client's console. 313 * @param message The message to add 314 * @param lineNumber the line on which the error occurred 315 * @param sourceID the filename of the source that caused the error. 316 * @param msgLevel the log level of this message. This is a value casted to int 317 * from WebCore::MessageLevel in WebCore/page/Console.h. 318 */ 319 protected void addMessageToConsole(String message, int lineNumber, String sourceID, 320 int msgLevel) { 321 mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID, msgLevel); 322 } 323 324 /** 325 * Invoke a javascript alert. 326 * @param message The message displayed in the alert. 327 */ 328 protected void jsAlert(String url, String message) { 329 mCallbackProxy.onJsAlert(url, message); 330 } 331 332 /** 333 * Called by JNI when the focus node changed. 334 */ 335 private void focusNodeChanged(int nodePointer, WebKitHitTest hitTest) { 336 if (mWebViewClassic == null) return; 337 mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.FOCUS_NODE_CHANGED, 338 nodePointer, 0, hitTest).sendToTarget(); 339 } 340 341 /** 342 * Called by JNI to advance focus to the next view. 343 */ 344 private void chromeTakeFocus(int webkitDirection) { 345 if (mWebViewClassic == null) return; 346 Message m = mWebViewClassic.mPrivateHandler.obtainMessage( 347 WebViewClassic.TAKE_FOCUS); 348 m.arg1 = mapDirection(webkitDirection); 349 m.sendToTarget(); 350 } 351 352 /** 353 * Called by JNI to see if we can take focus in the given direction. 354 */ 355 private boolean chromeCanTakeFocus(int webkitDirection) { 356 int direction = mapDirection(webkitDirection); 357 return direction == mChromeCanFocusDirection && direction != 0; 358 } 359 360 /** 361 * Maps a Webkit focus direction to a framework one 362 */ 363 private int mapDirection(int webkitDirection) { 364 /* 365 * This is WebKit's FocusDirection enum (from FocusDirection.h) 366 enum FocusDirection { 367 FocusDirectionNone = 0, 368 FocusDirectionForward, 369 FocusDirectionBackward, 370 FocusDirectionUp, 371 FocusDirectionDown, 372 FocusDirectionLeft, 373 FocusDirectionRight 374 }; 375 */ 376 switch (webkitDirection) { 377 case 1: 378 return View.FOCUS_FORWARD; 379 case 2: 380 return View.FOCUS_BACKWARD; 381 case 3: 382 return View.FOCUS_UP; 383 case 4: 384 return View.FOCUS_DOWN; 385 case 5: 386 return View.FOCUS_LEFT; 387 case 6: 388 return View.FOCUS_RIGHT; 389 } 390 return 0; 391 } 392 393 /** 394 * Called by JNI. Open a file chooser to upload a file. 395 * @param acceptType The value of the 'accept' attribute of the 396 * input tag associated with this file picker. 397 * @param capture The value of the 'capture' attribute of the 398 * input tag associated with this file picker. 399 * @return String version of the URI. 400 */ 401 private String openFileChooser(String acceptType, String capture) { 402 Uri uri = mCallbackProxy.openFileChooser(acceptType, capture); 403 if (uri != null) { 404 String filePath = ""; 405 // Note - querying for MediaStore.Images.Media.DATA 406 // seems to work for all content URIs, not just images 407 Cursor cursor = mContext.getContentResolver().query( 408 uri, 409 new String[] { MediaStore.Images.Media.DATA }, 410 null, null, null); 411 if (cursor != null) { 412 try { 413 if (cursor.moveToNext()) { 414 filePath = cursor.getString(0); 415 } 416 } finally { 417 cursor.close(); 418 } 419 } else { 420 filePath = uri.getLastPathSegment(); 421 } 422 String uriString = uri.toString(); 423 BrowserFrame.sJavaBridge.storeFilePathForContentUri(filePath, uriString); 424 return uriString; 425 } 426 return ""; 427 } 428 429 /** 430 * Notify the browser that the origin has exceeded it's database quota. 431 * @param url The URL that caused the overflow. 432 * @param databaseIdentifier The identifier of the database. 433 * @param quota The current quota for the origin. 434 * @param estimatedDatabaseSize The estimated size of the database. 435 */ 436 protected void exceededDatabaseQuota(String url, 437 String databaseIdentifier, 438 long quota, 439 long estimatedDatabaseSize) { 440 // Inform the callback proxy of the quota overflow. Send an object 441 // that encapsulates a call to the nativeSetDatabaseQuota method to 442 // awaken the sleeping webcore thread when a decision from the 443 // client to allow or deny quota is available. 444 mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier, 445 quota, estimatedDatabaseSize, getUsedQuota(), 446 new WebStorage.QuotaUpdater() { 447 @Override 448 public void updateQuota(long newQuota) { 449 nativeSetNewStorageLimit(mNativeClass, newQuota); 450 } 451 }); 452 } 453 454 /** 455 * Notify the browser that the appcache has exceeded its max size. 456 * @param requiredStorage is the amount of storage, in bytes, that would be 457 * needed in order for the last appcache operation to succeed. 458 */ 459 protected void reachedMaxAppCacheSize(long requiredStorage) { 460 mCallbackProxy.onReachedMaxAppCacheSize(requiredStorage, getUsedQuota(), 461 new WebStorage.QuotaUpdater() { 462 @Override 463 public void updateQuota(long newQuota) { 464 nativeSetNewStorageLimit(mNativeClass, newQuota); 465 } 466 }); 467 } 468 469 protected void populateVisitedLinks() { 470 ValueCallback callback = new ValueCallback<String[]>() { 471 @Override 472 public void onReceiveValue(String[] value) { 473 sendMessage(EventHub.POPULATE_VISITED_LINKS, (Object)value); 474 } 475 }; 476 mCallbackProxy.getVisitedHistory(callback); 477 } 478 479 /** 480 * Shows a prompt to ask the user to set the Geolocation permission state 481 * for the given origin. 482 * @param origin The origin for which Geolocation permissions are 483 * requested. 484 */ 485 protected void geolocationPermissionsShowPrompt(String origin) { 486 mCallbackProxy.onGeolocationPermissionsShowPrompt(origin, 487 new GeolocationPermissions.Callback() { 488 @Override 489 public void invoke(String origin, boolean allow, boolean remember) { 490 GeolocationPermissionsData data = new GeolocationPermissionsData(); 491 data.mOrigin = origin; 492 data.mAllow = allow; 493 data.mRemember = remember; 494 // Marshall to WebCore thread. 495 sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data); 496 } 497 }); 498 } 499 500 /** 501 * Hides the Geolocation permissions prompt. 502 */ 503 protected void geolocationPermissionsHidePrompt() { 504 mCallbackProxy.onGeolocationPermissionsHidePrompt(); 505 } 506 507 /** 508 * Invoke a javascript confirm dialog. 509 * @param message The message displayed in the dialog. 510 * @return True if the user confirmed or false if the user cancelled. 511 */ 512 protected boolean jsConfirm(String url, String message) { 513 return mCallbackProxy.onJsConfirm(url, message); 514 } 515 516 /** 517 * Invoke a javascript prompt dialog. 518 * @param message The message to be displayed in the dialog. 519 * @param defaultValue The default value in the prompt input. 520 * @return The input from the user or null to indicate the user cancelled 521 * the dialog. 522 */ 523 protected String jsPrompt(String url, String message, String defaultValue) { 524 return mCallbackProxy.onJsPrompt(url, message, defaultValue); 525 } 526 527 /** 528 * Invoke a javascript before unload dialog. 529 * @param url The url that is requesting the dialog. 530 * @param message The message displayed in the dialog. 531 * @return True if the user confirmed or false if the user cancelled. False 532 * will cancel the navigation. 533 */ 534 protected boolean jsUnload(String url, String message) { 535 return mCallbackProxy.onJsBeforeUnload(url, message); 536 } 537 538 /** 539 * 540 * Callback to notify that a JavaScript execution timeout has occured. 541 * @return True if the JavaScript execution should be interrupted. False 542 * will continue the execution. 543 */ 544 protected boolean jsInterrupt() { 545 return mCallbackProxy.onJsTimeout(); 546 } 547 548 /** 549 * Notify the webview that this is an installable web app. 550 */ 551 protected void setInstallableWebApp() { 552 mCallbackProxy.setInstallableWebApp(); 553 } 554 555 /** 556 * Notify the webview that we want to display the video layer fullscreen. 557 */ 558 protected void enterFullscreenForVideoLayer(int layerId, String url) { 559 if (mWebViewClassic == null) return; 560 Message message = Message.obtain(mWebViewClassic.mPrivateHandler, 561 WebViewClassic.ENTER_FULLSCREEN_VIDEO, layerId, 0); 562 message.obj = url; 563 message.sendToTarget(); 564 } 565 566 /** 567 * Notify the webview that we want to exit the video fullscreen. 568 * This is called through JNI by webcore. 569 */ 570 protected void exitFullscreenVideo() { 571 if (mWebViewClassic == null) return; 572 Message message = Message.obtain(mWebViewClassic.mPrivateHandler, 573 WebViewClassic.EXIT_FULLSCREEN_VIDEO); 574 message.sendToTarget(); 575 } 576 577 /** 578 * Clear the picture set. To be called only on the WebCore thread. 579 */ 580 /* package */ void clearContent() { 581 nativeClearContent(mNativeClass); 582 } 583 584 //------------------------------------------------------------------------- 585 // JNI methods 586 //------------------------------------------------------------------------- 587 588 static native String nativeFindAddress(String addr, boolean caseInsensitive); 589 590 /** 591 * Empty the picture set. 592 */ 593 private native void nativeClearContent(int nativeClass); 594 595 private native void nativeContentInvalidateAll(int nativeClass); 596 597 /** 598 * Redraw a portion of the picture set. The Point wh returns the 599 * width and height of the overall picture. 600 */ 601 private native int nativeRecordContent(int nativeClass, Region invalRegion, 602 Point wh); 603 604 /** 605 * Notify webkit that animations have begun (on the hardware accelerated content) 606 */ 607 private native void nativeNotifyAnimationStarted(int nativeClass); 608 609 private native boolean nativeFocusBoundsChanged(int nativeClass); 610 611 /** 612 * Splits slow parts of the picture set. Called from the webkit thread after 613 * WebView.nativeDraw() returns content to be split. 614 */ 615 private native void nativeSplitContent(int nativeClass, int content); 616 617 private native boolean nativeKey(int nativeClass, int keyCode, 618 int unichar, int repeatCount, boolean isShift, boolean isAlt, 619 boolean isSym, boolean isDown); 620 621 private native void nativeSendListBoxChoices(int nativeClass, 622 boolean[] choices, int size); 623 624 private native void nativeSendListBoxChoice(int nativeClass, int choice); 625 626 private native void nativeCloseIdleConnections(int nativeClass); 627 628 /* Tell webkit what its width and height are, for the purposes 629 of layout/line-breaking. These coordinates are in document space, 630 which is the same as View coords unless we have zoomed the document 631 (see nativeSetZoom). 632 textWrapWidth is used by layout to wrap column around. If viewport uses 633 fixed size, textWrapWidth can be different from width with zooming. 634 should this be called nativeSetViewPortSize? 635 */ 636 private native void nativeSetSize(int nativeClass, int width, int height, 637 int textWrapWidth, float scale, int screenWidth, int screenHeight, 638 int anchorX, int anchorY, boolean ignoreHeight); 639 640 private native int nativeGetContentMinPrefWidth(int nativeClass); 641 642 // Start: functions that deal with text editing 643 private native void nativeReplaceTextfieldText( 644 int nativeClass, int oldStart, int oldEnd, String replace, 645 int newStart, int newEnd, int textGeneration); 646 647 private native void passToJs(int nativeClass, 648 int gen, String currentText, int keyCode, int keyValue, 649 boolean down, boolean cap, boolean fn, boolean sym); 650 651 private native void nativeSetFocusControllerActive(int nativeClass, 652 boolean active); 653 654 private native void nativeSaveDocumentState(int nativeClass); 655 656 private native void nativeMoveMouse(int nativeClass, int x, int y); 657 658 private native String nativeRetrieveHref(int nativeClass, int x, int y); 659 private native String nativeRetrieveAnchorText(int nativeClass, 660 int x, int y); 661 private native String nativeRetrieveImageSource(int nativeClass, 662 int x, int y); 663 private native boolean nativeMouseClick(int nativeClass); 664 665 private native boolean nativeHandleTouchEvent(int nativeClass, int action, 666 int[] idArray, int[] xArray, int[] yArray, int count, 667 int actionIndex, int metaState); 668 669 private native void nativeSetBackgroundColor(int nativeClass, int color); 670 671 private native void nativeDumpDomTree(int nativeClass, boolean useFile); 672 673 private native void nativeDumpRenderTree(int nativeClass, boolean useFile); 674 675 private native void nativeSetJsFlags(int nativeClass, String flags); 676 677 /** 678 * Delete text from start to end in the focused textfield. If there is no 679 * focus, or if start == end, silently fail. If start and end are out of 680 * order, swap them. 681 * @param nativeClass Pointer to the C++ WebViewCore object mNativeClass 682 * @param start Beginning of selection to delete. 683 * @param end End of selection to delete. 684 * @param textGeneration Text generation number when delete was pressed. 685 */ 686 private native void nativeDeleteSelection(int nativeClass, int start, 687 int end, int textGeneration); 688 689 /** 690 * Set the selection to (start, end) in the focused textfield. If start and 691 * end are out of order, swap them. 692 * @param nativeClass Pointer to the C++ WebViewCore object mNativeClass 693 * @param start Beginning of selection. 694 * @param end End of selection. 695 */ 696 private native void nativeSetSelection(int nativeClass, int start, int end); 697 698 // Register a scheme to be treated as local scheme so that it can access 699 // local asset files for resources 700 private native void nativeRegisterURLSchemeAsLocal(int nativeClass, 701 String scheme); 702 703 /* 704 * Inform webcore that the user has decided whether to allow or deny new 705 * quota for the current origin or more space for the app cache, and that 706 * the main thread should wake up now. 707 * @param limit Is the new quota for an origin or new app cache max size. 708 */ 709 private native void nativeSetNewStorageLimit(int nativeClass, long limit); 710 711 /** 712 * Provide WebCore with a Geolocation permission state for the specified 713 * origin. 714 * @param nativeClass Pointer to the C++ WebViewCore object mNativeClass 715 * @param origin The origin for which Geolocation permissions are provided. 716 * @param allow Whether Geolocation permissions are allowed. 717 * @param remember Whether this decision should be remembered beyond the 718 * life of the current page. 719 */ 720 private native void nativeGeolocationPermissionsProvide(int nativeClass, 721 String origin, boolean allow, boolean remember); 722 723 /** 724 * Provide WebCore with the previously visted links from the history database 725 * @param nativeClass TODO 726 */ 727 private native void nativeProvideVisitedHistory(int nativeClass, 728 String[] history); 729 730 /** 731 * Modifies the current selection. 732 * 733 * Note: Accessibility support. 734 * @param nativeClass Pointer to the C++ WebViewCore object mNativeClass 735 * @param direction The direction in which to alter the selection. 736 * @param granularity The granularity of the selection modification. 737 * 738 * @return The selection string. 739 */ 740 private native String nativeModifySelection(int nativeClass, int direction, 741 int granularity); 742 743 // EventHub for processing messages 744 private final EventHub mEventHub; 745 // WebCore thread handler 746 private static Handler sWebCoreHandler; 747 // Class for providing Handler creation inside the WebCore thread. 748 private static class WebCoreThread implements Runnable { 749 // Message id for initializing a new WebViewCore. 750 private static final int INITIALIZE = 0; 751 private static final int REDUCE_PRIORITY = 1; 752 private static final int RESUME_PRIORITY = 2; 753 754 @Override 755 public void run() { 756 Looper.prepare(); 757 Assert.assertNull(sWebCoreHandler); 758 synchronized (WebViewCore.class) { 759 sWebCoreHandler = new Handler() { 760 @Override 761 public void handleMessage(Message msg) { 762 switch (msg.what) { 763 case INITIALIZE: 764 WebViewCore core = (WebViewCore) msg.obj; 765 core.initialize(); 766 break; 767 768 case REDUCE_PRIORITY: 769 // 3 is an adjustable number. 770 Process.setThreadPriority( 771 Process.THREAD_PRIORITY_DEFAULT + 3 * 772 Process.THREAD_PRIORITY_LESS_FAVORABLE); 773 break; 774 775 case RESUME_PRIORITY: 776 Process.setThreadPriority( 777 Process.THREAD_PRIORITY_DEFAULT); 778 break; 779 780 case EventHub.ADD_PACKAGE_NAME: 781 if (BrowserFrame.sJavaBridge == null) { 782 throw new IllegalStateException( 783 "No WebView has been created in this process!"); 784 } 785 BrowserFrame.sJavaBridge.addPackageName((String) msg.obj); 786 break; 787 788 case EventHub.REMOVE_PACKAGE_NAME: 789 if (BrowserFrame.sJavaBridge == null) { 790 throw new IllegalStateException( 791 "No WebView has been created in this process!"); 792 } 793 BrowserFrame.sJavaBridge.removePackageName((String) msg.obj); 794 break; 795 796 case EventHub.PROXY_CHANGED: 797 if (BrowserFrame.sJavaBridge == null) { 798 throw new IllegalStateException( 799 "No WebView has been created in this process!"); 800 } 801 BrowserFrame.sJavaBridge.updateProxy((ProxyProperties)msg.obj); 802 break; 803 804 case EventHub.HEARTBEAT: 805 // Ping back the watchdog to let it know we're still processing 806 // messages. 807 Message m = (Message)msg.obj; 808 m.sendToTarget(); 809 break; 810 case EventHub.TRUST_STORAGE_UPDATED: 811 // post a task to network thread for updating trust manager 812 nativeCertTrustChanged(); 813 CertificateChainValidator.handleTrustStorageUpdate(); 814 break; 815 } 816 } 817 }; 818 WebViewCore.class.notify(); 819 } 820 Looper.loop(); 821 } 822 } 823 824 static class BaseUrlData { 825 String mBaseUrl; 826 String mData; 827 String mMimeType; 828 String mEncoding; 829 String mHistoryUrl; 830 } 831 832 static class JSInterfaceData { 833 Object mObject; 834 String mInterfaceName; 835 } 836 837 static class JSKeyData { 838 String mCurrentText; 839 KeyEvent mEvent; 840 } 841 842 static class MotionUpData { 843 int mFrame; 844 int mNode; 845 Rect mBounds; 846 int mX; 847 int mY; 848 } 849 850 static class GetUrlData { 851 String mUrl; 852 Map<String, String> mExtraHeaders; 853 } 854 855 static class PostUrlData { 856 String mUrl; 857 byte[] mPostData; 858 } 859 860 static class ReplaceTextData { 861 String mReplace; 862 int mNewStart; 863 int mNewEnd; 864 int mTextGeneration; 865 } 866 867 static class TextSelectionData { 868 public TextSelectionData(int start, int end, int selectTextPtr) { 869 mStart = start; 870 mEnd = end; 871 mSelectTextPtr = selectTextPtr; 872 } 873 int mStart; 874 int mEnd; 875 int mSelectTextPtr; 876 } 877 878 static class TouchUpData { 879 int mMoveGeneration; 880 int mFrame; 881 int mNode; 882 int mX; 883 int mY; 884 int mNativeLayer; 885 Rect mNativeLayerRect = new Rect(); 886 } 887 888 static class TouchHighlightData { 889 int mX; 890 int mY; 891 int mSlop; 892 int mNativeLayer; 893 Rect mNativeLayerRect; 894 } 895 896 static class WebKitHitTest { 897 String mLinkUrl; 898 String mIntentUrl; 899 String mAnchorText; 900 String mImageUrl; 901 String mAltDisplayString; 902 String mTitle; 903 Rect[] mTouchRects; 904 boolean mEditable; 905 int mTapHighlightColor = WebViewClassic.HIGHLIGHT_COLOR; 906 Rect[] mEnclosingParentRects; 907 boolean mHasFocus; 908 909 // These are the input values that produced this hit test 910 int mHitTestX; 911 int mHitTestY; 912 int mHitTestSlop; 913 boolean mHitTestMovedMouse; 914 } 915 916 static class AutoFillData { 917 public AutoFillData() { 918 mQueryId = WebTextView.FORM_NOT_AUTOFILLABLE; 919 mPreview = ""; 920 } 921 922 public AutoFillData(int queryId, String preview) { 923 mQueryId = queryId; 924 mPreview = preview; 925 } 926 927 public int getQueryId() { 928 return mQueryId; 929 } 930 931 public String getPreviewString() { 932 return mPreview; 933 } 934 935 private int mQueryId; 936 private String mPreview; 937 } 938 939 static class TextFieldInitData { 940 public int mFieldPointer; 941 public String mText; 942 public int mType; 943 public boolean mIsSpellCheckEnabled; 944 public boolean mIsTextFieldNext; 945 public boolean mIsTextFieldPrev; 946 public boolean mIsAutoCompleteEnabled; 947 public String mName; 948 public String mLabel; 949 public int mMaxLength; 950 public Rect mContentBounds; 951 public int mNodeLayerId; 952 public Rect mContentRect; 953 } 954 955 // mAction of TouchEventData can be MotionEvent.getAction() which uses the 956 // last two bytes or one of the following values 957 static final int ACTION_LONGPRESS = 0x100; 958 static final int ACTION_DOUBLETAP = 0x200; 959 960 static class TouchEventData { 961 int mAction; 962 int[] mIds; // Ids of the touch points 963 Point[] mPoints; 964 Point[] mPointsInView; // the point coordinates in view axis. 965 int mActionIndex; // Associated pointer index for ACTION_POINTER_DOWN/UP 966 int mMetaState; 967 boolean mReprocess; 968 MotionEvent mMotionEvent; 969 int mNativeLayer; 970 Rect mNativeLayerRect = new Rect(); 971 long mSequence; 972 boolean mNativeResult; 973 } 974 975 static class GeolocationPermissionsData { 976 String mOrigin; 977 boolean mAllow; 978 boolean mRemember; 979 } 980 981 static final String[] HandlerDebugString = { 982 "REVEAL_SELECTION", // 96 983 "", // 97 984 "", // = 98 985 "SCROLL_TEXT_INPUT", // = 99 986 "LOAD_URL", // = 100; 987 "STOP_LOADING", // = 101; 988 "RELOAD", // = 102; 989 "KEY_DOWN", // = 103; 990 "KEY_UP", // = 104; 991 "VIEW_SIZE_CHANGED", // = 105; 992 "GO_BACK_FORWARD", // = 106; 993 "SET_SCROLL_OFFSET", // = 107; 994 "RESTORE_STATE", // = 108; 995 "PAUSE_TIMERS", // = 109; 996 "RESUME_TIMERS", // = 110; 997 "CLEAR_CACHE", // = 111; 998 "CLEAR_HISTORY", // = 112; 999 "SET_SELECTION", // = 113; 1000 "REPLACE_TEXT", // = 114; 1001 "PASS_TO_JS", // = 115; 1002 "SET_GLOBAL_BOUNDS", // = 116; 1003 "", // = 117; 1004 "CLICK", // = 118; 1005 "SET_NETWORK_STATE", // = 119; 1006 "DOC_HAS_IMAGES", // = 120; 1007 "FAKE_CLICK", // = 121; 1008 "DELETE_SELECTION", // = 122; 1009 "LISTBOX_CHOICES", // = 123; 1010 "SINGLE_LISTBOX_CHOICE", // = 124; 1011 "MESSAGE_RELAY", // = 125; 1012 "SET_BACKGROUND_COLOR", // = 126; 1013 "SET_MOVE_FOCUS", // = 127 1014 "SAVE_DOCUMENT_STATE", // = 128; 1015 "129", // = 129; 1016 "WEBKIT_DRAW", // = 130; 1017 "131", // = 131; 1018 "POST_URL", // = 132; 1019 "SPLIT_PICTURE_SET", // = 133; 1020 "CLEAR_CONTENT", // = 134; 1021 "", // = 135; 1022 "", // = 136; 1023 "REQUEST_CURSOR_HREF", // = 137; 1024 "ADD_JS_INTERFACE", // = 138; 1025 "LOAD_DATA", // = 139; 1026 "", // = 140; 1027 "", // = 141; 1028 "SET_ACTIVE", // = 142; 1029 "ON_PAUSE", // = 143 1030 "ON_RESUME", // = 144 1031 "FREE_MEMORY", // = 145 1032 "VALID_NODE_BOUNDS", // = 146 1033 "SAVE_WEBARCHIVE", // = 147 1034 "WEBKIT_DRAW_LAYERS", // = 148; 1035 "REMOVE_JS_INTERFACE", // = 149; 1036 }; 1037 1038 static class FindAllRequest { 1039 public FindAllRequest(String text) { 1040 mSearchText = text; 1041 mMatchCount = -1; 1042 mMatchIndex = -1; 1043 } 1044 public final String mSearchText; 1045 public int mMatchCount; 1046 public int mMatchIndex; 1047 } 1048 1049 /** 1050 * @hide 1051 */ 1052 public class EventHub implements WebViewInputDispatcher.WebKitCallbacks { 1053 // Message Ids 1054 static final int REVEAL_SELECTION = 96; 1055 static final int SCROLL_TEXT_INPUT = 99; 1056 static final int LOAD_URL = 100; 1057 static final int STOP_LOADING = 101; 1058 static final int RELOAD = 102; 1059 static final int KEY_DOWN = 103; 1060 static final int KEY_UP = 104; 1061 static final int VIEW_SIZE_CHANGED = 105; 1062 static final int GO_BACK_FORWARD = 106; 1063 static final int SET_SCROLL_OFFSET = 107; 1064 static final int RESTORE_STATE = 108; 1065 static final int PAUSE_TIMERS = 109; 1066 static final int RESUME_TIMERS = 110; 1067 static final int CLEAR_CACHE = 111; 1068 static final int CLEAR_HISTORY = 112; 1069 static final int SET_SELECTION = 113; 1070 static final int REPLACE_TEXT = 114; 1071 static final int PASS_TO_JS = 115; 1072 static final int SET_GLOBAL_BOUNDS = 116; 1073 static final int SET_NETWORK_STATE = 119; 1074 static final int DOC_HAS_IMAGES = 120; 1075 static final int DELETE_SELECTION = 122; 1076 static final int LISTBOX_CHOICES = 123; 1077 static final int SINGLE_LISTBOX_CHOICE = 124; 1078 public static final int MESSAGE_RELAY = 125; 1079 static final int SET_BACKGROUND_COLOR = 126; 1080 static final int SAVE_DOCUMENT_STATE = 128; 1081 static final int DELETE_SURROUNDING_TEXT = 129; 1082 1083 1084 static final int WEBKIT_DRAW = 130; 1085 static final int POST_URL = 132; 1086 static final int SPLIT_PICTURE_SET = 133; 1087 static final int CLEAR_CONTENT = 134; 1088 1089 // UI nav messages 1090 static final int SET_MOVE_MOUSE = 135; 1091 static final int REQUEST_CURSOR_HREF = 137; 1092 static final int ADD_JS_INTERFACE = 138; 1093 static final int LOAD_DATA = 139; 1094 1095 // Used to tell the focus controller not to draw the blinking cursor, 1096 // based on whether the WebView has focus and whether the WebView's 1097 // cursor matches the webpage's focus. 1098 static final int SET_ACTIVE = 142; 1099 1100 // lifecycle activities for just this DOM (unlike pauseTimers, which 1101 // is global) 1102 static final int ON_PAUSE = 143; 1103 static final int ON_RESUME = 144; 1104 static final int FREE_MEMORY = 145; 1105 1106 // Load and save web archives 1107 static final int SAVE_WEBARCHIVE = 147; 1108 1109 static final int REMOVE_JS_INTERFACE = 149; 1110 1111 // Network-based messaging 1112 static final int CLEAR_SSL_PREF_TABLE = 150; 1113 1114 // Test harness messages 1115 static final int REQUEST_EXT_REPRESENTATION = 160; 1116 static final int REQUEST_DOC_AS_TEXT = 161; 1117 1118 // debugging 1119 static final int DUMP_DOMTREE = 170; 1120 static final int DUMP_RENDERTREE = 171; 1121 1122 static final int SET_JS_FLAGS = 174; 1123 static final int CONTENT_INVALIDATE_ALL = 175; 1124 // Geolocation 1125 static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180; 1126 1127 static final int POPULATE_VISITED_LINKS = 181; 1128 1129 static final int HIDE_FULLSCREEN = 182; 1130 1131 static final int SET_NETWORK_TYPE = 183; 1132 1133 // navigator.isApplicationInstalled() 1134 static final int ADD_PACKAGE_NAMES = 184; 1135 static final int ADD_PACKAGE_NAME = 185; 1136 static final int REMOVE_PACKAGE_NAME = 186; 1137 1138 static final int HIT_TEST = 187; 1139 1140 // accessibility support 1141 static final int MODIFY_SELECTION = 190; 1142 1143 static final int SET_USE_MOCK_DEVICE_ORIENTATION = 191; 1144 1145 static final int AUTOFILL_FORM = 192; 1146 1147 static final int PROXY_CHANGED = 193; 1148 1149 static final int EXECUTE_JS = 194; 1150 1151 static final int PLUGIN_SURFACE_READY = 195; 1152 1153 static final int NOTIFY_ANIMATION_STARTED = 196; 1154 1155 static final int HEARTBEAT = 197; 1156 1157 static final int SCROLL_LAYER = 198; 1158 1159 // private message ids 1160 private static final int DESTROY = 200; 1161 1162 // for cut & paste 1163 static final int COPY_TEXT = 210; 1164 static final int DELETE_TEXT = 211; 1165 static final int INSERT_TEXT = 212; 1166 static final int SELECT_TEXT = 213; 1167 static final int SELECT_WORD_AT = 214; 1168 static final int SELECT_ALL = 215; 1169 1170 // for updating state on trust storage change 1171 static final int TRUST_STORAGE_UPDATED = 220; 1172 1173 // find-on-page controls 1174 static final int FIND_ALL = 221; 1175 static final int FIND_NEXT = 222; 1176 1177 // key was pressed (down and up) 1178 static final int KEY_PRESS = 223; 1179 static final int SET_INITIAL_FOCUS = 224; 1180 1181 // Private handler for WebCore messages. 1182 private Handler mHandler; 1183 // Message queue for containing messages before the WebCore thread is 1184 // ready. 1185 private ArrayList<Message> mMessages = new ArrayList<Message>(); 1186 // Flag for blocking messages. This is used during DESTROY to avoid 1187 // posting more messages to the EventHub or to WebView's event handler. 1188 private boolean mBlockMessages; 1189 private boolean mDestroying; 1190 1191 private int mTid; 1192 private int mSavedPriority; 1193 1194 /** 1195 * Prevent other classes from creating an EventHub. 1196 */ 1197 private EventHub() {} 1198 1199 private static final int FIRST_PACKAGE_MSG_ID = REVEAL_SELECTION; 1200 private static final int LAST_PACKAGE_MSG_ID = REMOVE_JS_INTERFACE; 1201 1202 /** 1203 * Transfer all messages to the newly created webcore thread handler. 1204 */ 1205 private void transferMessages() { 1206 mTid = Process.myTid(); 1207 mSavedPriority = Process.getThreadPriority(mTid); 1208 1209 mHandler = new Handler() { 1210 @Override 1211 public void handleMessage(Message msg) { 1212 if (DebugFlags.WEB_VIEW_CORE) { 1213 Log.v(LOGTAG, (msg.what < FIRST_PACKAGE_MSG_ID 1214 || msg.what > LAST_PACKAGE_MSG_ID 1215 ? Integer.toString(msg.what) 1216 : HandlerDebugString[msg.what 1217 - FIRST_PACKAGE_MSG_ID]) 1218 + " arg1=" + msg.arg1 + " arg2=" + msg.arg2 1219 + " obj=" + msg.obj); 1220 } 1221 switch (msg.what) { 1222 case PAUSE_TIMERS: 1223 mSavedPriority = Process.getThreadPriority(mTid); 1224 Process.setThreadPriority(mTid, 1225 Process.THREAD_PRIORITY_BACKGROUND); 1226 pauseTimers(); 1227 if (mNativeClass != 0) { 1228 nativeCloseIdleConnections(mNativeClass); 1229 } 1230 return; 1231 1232 case RESUME_TIMERS: 1233 Process.setThreadPriority(mTid, mSavedPriority); 1234 resumeTimers(); 1235 return; 1236 } 1237 1238 if (mWebViewClassic == null || mNativeClass == 0) { 1239 if (DebugFlags.WEB_VIEW_CORE) { 1240 Log.w(LOGTAG, "Rejecting message " + msg.what 1241 + " because we are destroyed"); 1242 } 1243 return; 1244 } 1245 if (mDestroying == true 1246 && msg.what != EventHub.DESTROY) { 1247 if (DebugFlags.WEB_VIEW_CORE) { 1248 Log.v(LOGTAG, "Rejecting message " + msg.what 1249 + " because we are being destroyed"); 1250 } 1251 return; 1252 } 1253 switch (msg.what) { 1254 case WEBKIT_DRAW: 1255 webkitDraw(); 1256 break; 1257 1258 case DESTROY: 1259 // Time to take down the world. Cancel all pending 1260 // loads and destroy the native view and frame. 1261 synchronized (WebViewCore.this) { 1262 mCallbackProxy.shutdown(); 1263 // Wake up the WebCore thread just in case it is waiting for a 1264 // JavaScript dialog. 1265 synchronized (mCallbackProxy) { 1266 mCallbackProxy.notify(); 1267 } 1268 mBrowserFrame.destroy(); 1269 mBrowserFrame = null; 1270 mSettings.onDestroyed(); 1271 mNativeClass = 0; 1272 mWebViewClassic = null; 1273 } 1274 break; 1275 1276 case REVEAL_SELECTION: 1277 nativeRevealSelection(mNativeClass); 1278 break; 1279 1280 case SCROLL_TEXT_INPUT: 1281 float xPercent; 1282 if (msg.obj == null) { 1283 xPercent = 0f; 1284 } else { 1285 xPercent = ((Float) msg.obj).floatValue(); 1286 } 1287 Rect contentBounds = new Rect(); 1288 nativeScrollFocusedTextInput(mNativeClass, xPercent, 1289 msg.arg2, contentBounds); 1290 Message.obtain( 1291 mWebViewClassic.mPrivateHandler, 1292 WebViewClassic.UPDATE_CONTENT_BOUNDS, 1293 contentBounds).sendToTarget(); 1294 break; 1295 1296 case LOAD_URL: { 1297 CookieManager.getInstance().waitForCookieOperationsToComplete(); 1298 GetUrlData param = (GetUrlData) msg.obj; 1299 loadUrl(param.mUrl, param.mExtraHeaders); 1300 break; 1301 } 1302 1303 case POST_URL: { 1304 CookieManager.getInstance().waitForCookieOperationsToComplete(); 1305 PostUrlData param = (PostUrlData) msg.obj; 1306 mBrowserFrame.postUrl(param.mUrl, param.mPostData); 1307 break; 1308 } 1309 case LOAD_DATA: 1310 CookieManager.getInstance().waitForCookieOperationsToComplete(); 1311 BaseUrlData loadParams = (BaseUrlData) msg.obj; 1312 String baseUrl = loadParams.mBaseUrl; 1313 if (baseUrl != null) { 1314 int i = baseUrl.indexOf(':'); 1315 if (i > 0) { 1316 // In 1.0, WebView.loadDataWithBaseURL() could access local 1317 // asset files using 'file' scheme URLs as long as the data is 1318 // valid. Later versions of WebKit have tightened the 1319 // restriction around when pages can access such local URLs. 1320 // To maintain compatibility with 1.0, we register the scheme of 1321 // the baseUrl to be considered local, as long as it is not 1322 // http(s)/ftp(s)/about/javascript. 1323 String scheme = baseUrl.substring(0, i); 1324 if (!scheme.startsWith("http") && 1325 !scheme.startsWith("ftp") && 1326 !scheme.startsWith("about") && 1327 !scheme.startsWith("javascript")) { 1328 nativeRegisterURLSchemeAsLocal(mNativeClass, 1329 scheme); 1330 } 1331 } 1332 } 1333 mBrowserFrame.loadData(baseUrl, 1334 loadParams.mData, 1335 loadParams.mMimeType, 1336 loadParams.mEncoding, 1337 loadParams.mHistoryUrl); 1338 nativeContentInvalidateAll(mNativeClass); 1339 break; 1340 1341 case STOP_LOADING: 1342 // If the WebCore has committed the load, but not 1343 // finished the first layout yet, we need to set 1344 // first layout done to trigger the interpreted side sync 1345 // up with native side 1346 if (mBrowserFrame.committed() 1347 && !mBrowserFrame.firstLayoutDone()) { 1348 mBrowserFrame.didFirstLayout(); 1349 } 1350 // Do this after syncing up the layout state. 1351 stopLoading(); 1352 break; 1353 1354 case RELOAD: 1355 mBrowserFrame.reload(false); 1356 break; 1357 1358 case KEY_DOWN: 1359 key((KeyEvent) msg.obj, msg.arg1, true); 1360 break; 1361 1362 case KEY_UP: 1363 key((KeyEvent) msg.obj, msg.arg1, false); 1364 break; 1365 1366 case KEY_PRESS: 1367 keyPress(msg.arg1); 1368 break; 1369 1370 case VIEW_SIZE_CHANGED: { 1371 viewSizeChanged((WebViewClassic.ViewSizeData) msg.obj); 1372 break; 1373 } 1374 case SET_SCROLL_OFFSET: 1375 // note: these are in document coordinates 1376 // (inv-zoom) 1377 Point pt = (Point) msg.obj; 1378 nativeSetScrollOffset(mNativeClass, 1379 msg.arg1 == 1, pt.x, pt.y); 1380 break; 1381 1382 case SET_GLOBAL_BOUNDS: 1383 Rect r = (Rect) msg.obj; 1384 nativeSetGlobalBounds(mNativeClass, r.left, r.top, 1385 r.width(), r.height()); 1386 break; 1387 1388 case GO_BACK_FORWARD: 1389 // If it is a standard load and the load is not 1390 // committed yet, we interpret BACK as RELOAD 1391 if (!mBrowserFrame.committed() && msg.arg1 == -1 && 1392 (mBrowserFrame.loadType() == 1393 BrowserFrame.FRAME_LOADTYPE_STANDARD)) { 1394 mBrowserFrame.reload(true); 1395 } else { 1396 mBrowserFrame.goBackOrForward(msg.arg1); 1397 } 1398 break; 1399 1400 case RESTORE_STATE: 1401 stopLoading(); 1402 restoreState(msg.arg1); 1403 break; 1404 1405 1406 case ON_PAUSE: 1407 nativePause(mNativeClass); 1408 break; 1409 1410 case ON_RESUME: 1411 nativeResume(mNativeClass); 1412 break; 1413 1414 case FREE_MEMORY: 1415 clearCache(false); 1416 nativeFreeMemory(mNativeClass); 1417 break; 1418 1419 case SET_NETWORK_STATE: 1420 if (BrowserFrame.sJavaBridge == null) { 1421 throw new IllegalStateException("No WebView " + 1422 "has been created in this process!"); 1423 } 1424 BrowserFrame.sJavaBridge 1425 .setNetworkOnLine(msg.arg1 == 1); 1426 break; 1427 1428 case SET_NETWORK_TYPE: 1429 if (BrowserFrame.sJavaBridge == null) { 1430 throw new IllegalStateException("No WebView " + 1431 "has been created in this process!"); 1432 } 1433 Map<String, String> map = (Map<String, String>) msg.obj; 1434 BrowserFrame.sJavaBridge 1435 .setNetworkType(map.get("type"), map.get("subtype")); 1436 break; 1437 1438 case CLEAR_CACHE: 1439 clearCache(msg.arg1 == 1); 1440 break; 1441 1442 case CLEAR_HISTORY: 1443 mCallbackProxy.getBackForwardList(). 1444 close(mBrowserFrame.mNativeFrame); 1445 break; 1446 1447 case REPLACE_TEXT: 1448 ReplaceTextData rep = (ReplaceTextData) msg.obj; 1449 nativeReplaceTextfieldText(mNativeClass, msg.arg1, 1450 msg.arg2, rep.mReplace, rep.mNewStart, 1451 rep.mNewEnd, rep.mTextGeneration); 1452 break; 1453 1454 case PASS_TO_JS: { 1455 JSKeyData jsData = (JSKeyData) msg.obj; 1456 KeyEvent evt = jsData.mEvent; 1457 int keyCode = evt.getKeyCode(); 1458 int keyValue = evt.getUnicodeChar(); 1459 int generation = msg.arg1; 1460 passToJs(mNativeClass, 1461 generation, 1462 jsData.mCurrentText, 1463 keyCode, 1464 keyValue, 1465 evt.isDown(), evt.isShiftPressed(), 1466 evt.isAltPressed(), evt.isSymPressed()); 1467 break; 1468 } 1469 1470 case SAVE_DOCUMENT_STATE: { 1471 nativeSaveDocumentState(mNativeClass); 1472 break; 1473 } 1474 1475 case CLEAR_SSL_PREF_TABLE: 1476 // FIXME: This will not work for connections currently in use, as 1477 // they cache the certificate responses. See http://b/5324235. 1478 SslCertLookupTable.getInstance().clear(); 1479 nativeCloseIdleConnections(mNativeClass); 1480 break; 1481 1482 case SET_ACTIVE: 1483 nativeSetFocusControllerActive(mNativeClass, msg.arg1 == 1); 1484 break; 1485 1486 case ADD_JS_INTERFACE: 1487 JSInterfaceData jsData = (JSInterfaceData) msg.obj; 1488 mBrowserFrame.addJavascriptInterface(jsData.mObject, 1489 jsData.mInterfaceName); 1490 break; 1491 1492 case REMOVE_JS_INTERFACE: 1493 jsData = (JSInterfaceData) msg.obj; 1494 mBrowserFrame.removeJavascriptInterface( 1495 jsData.mInterfaceName); 1496 break; 1497 1498 case REQUEST_EXT_REPRESENTATION: 1499 mBrowserFrame.externalRepresentation( 1500 (Message) msg.obj); 1501 break; 1502 1503 case REQUEST_DOC_AS_TEXT: 1504 mBrowserFrame.documentAsText((Message) msg.obj); 1505 break; 1506 1507 case SET_MOVE_MOUSE: 1508 nativeMoveMouse(mNativeClass, msg.arg1, msg.arg2); 1509 break; 1510 1511 case REQUEST_CURSOR_HREF: { 1512 WebKitHitTest hit = performHitTest(msg.arg1, msg.arg2, 1, false); 1513 Message hrefMsg = (Message) msg.obj; 1514 Bundle data = hrefMsg.getData(); 1515 data.putString(FocusNodeHref.URL,hit.mLinkUrl); 1516 data.putString(FocusNodeHref.TITLE, hit.mAnchorText); 1517 data.putString(FocusNodeHref.SRC, hit.mImageUrl); 1518 hrefMsg.sendToTarget(); 1519 break; 1520 } 1521 1522 case DOC_HAS_IMAGES: 1523 Message imageResult = (Message) msg.obj; 1524 imageResult.arg1 = 1525 mBrowserFrame.documentHasImages() ? 1 : 0; 1526 imageResult.sendToTarget(); 1527 break; 1528 1529 case DELETE_SELECTION: 1530 TextSelectionData deleteSelectionData 1531 = (TextSelectionData) msg.obj; 1532 nativeDeleteSelection(mNativeClass, 1533 deleteSelectionData.mStart, deleteSelectionData.mEnd, msg.arg1); 1534 break; 1535 1536 case SET_SELECTION: 1537 nativeSetSelection(mNativeClass, msg.arg1, msg.arg2); 1538 break; 1539 1540 case MODIFY_SELECTION: 1541 String modifiedSelectionString = 1542 nativeModifySelection(mNativeClass, msg.arg1, 1543 msg.arg2); 1544 mWebViewClassic.mPrivateHandler.obtainMessage( 1545 WebViewClassic.SELECTION_STRING_CHANGED, 1546 modifiedSelectionString).sendToTarget(); 1547 break; 1548 1549 case LISTBOX_CHOICES: 1550 SparseBooleanArray choices = (SparseBooleanArray) 1551 msg.obj; 1552 int choicesSize = msg.arg1; 1553 boolean[] choicesArray = new boolean[choicesSize]; 1554 for (int c = 0; c < choicesSize; c++) { 1555 choicesArray[c] = choices.get(c); 1556 } 1557 nativeSendListBoxChoices(mNativeClass, 1558 choicesArray, choicesSize); 1559 break; 1560 1561 case SINGLE_LISTBOX_CHOICE: 1562 nativeSendListBoxChoice(mNativeClass, msg.arg1); 1563 break; 1564 1565 case SET_BACKGROUND_COLOR: 1566 nativeSetBackgroundColor(mNativeClass, msg.arg1); 1567 break; 1568 1569 case DUMP_DOMTREE: 1570 nativeDumpDomTree(mNativeClass, msg.arg1 == 1); 1571 break; 1572 1573 case DUMP_RENDERTREE: 1574 nativeDumpRenderTree(mNativeClass, msg.arg1 == 1); 1575 break; 1576 1577 case SET_JS_FLAGS: 1578 nativeSetJsFlags(mNativeClass, (String)msg.obj); 1579 break; 1580 1581 case CONTENT_INVALIDATE_ALL: 1582 nativeContentInvalidateAll(mNativeClass); 1583 break; 1584 1585 case SAVE_WEBARCHIVE: 1586 WebViewClassic.SaveWebArchiveMessage saveMessage = 1587 (WebViewClassic.SaveWebArchiveMessage)msg.obj; 1588 saveMessage.mResultFile = 1589 saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname); 1590 mWebViewClassic.mPrivateHandler.obtainMessage( 1591 WebViewClassic.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget(); 1592 break; 1593 1594 case GEOLOCATION_PERMISSIONS_PROVIDE: 1595 GeolocationPermissionsData data = 1596 (GeolocationPermissionsData) msg.obj; 1597 nativeGeolocationPermissionsProvide(mNativeClass, 1598 data.mOrigin, data.mAllow, data.mRemember); 1599 break; 1600 1601 case SPLIT_PICTURE_SET: 1602 nativeSplitContent(mNativeClass, msg.arg1); 1603 mWebViewClassic.mPrivateHandler.obtainMessage( 1604 WebViewClassic.REPLACE_BASE_CONTENT, msg.arg1, 0); 1605 mSplitPictureIsScheduled = false; 1606 break; 1607 1608 case CLEAR_CONTENT: 1609 // Clear the view so that onDraw() will draw nothing 1610 // but white background 1611 // (See public method WebView.clearView) 1612 clearContent(); 1613 break; 1614 1615 case MESSAGE_RELAY: 1616 ((Message) msg.obj).sendToTarget(); 1617 break; 1618 1619 case POPULATE_VISITED_LINKS: 1620 nativeProvideVisitedHistory(mNativeClass, (String[])msg.obj); 1621 break; 1622 1623 case HIDE_FULLSCREEN: 1624 nativeFullScreenPluginHidden(mNativeClass, msg.arg1); 1625 break; 1626 1627 case PLUGIN_SURFACE_READY: 1628 nativePluginSurfaceReady(mNativeClass); 1629 break; 1630 1631 case NOTIFY_ANIMATION_STARTED: 1632 nativeNotifyAnimationStarted(mNativeClass); 1633 break; 1634 1635 case ADD_PACKAGE_NAMES: 1636 if (BrowserFrame.sJavaBridge == null) { 1637 throw new IllegalStateException("No WebView " + 1638 "has been created in this process!"); 1639 } 1640 BrowserFrame.sJavaBridge.addPackageNames( 1641 (Set<String>) msg.obj); 1642 break; 1643 1644 case HIT_TEST: 1645 TouchHighlightData d = (TouchHighlightData) msg.obj; 1646 if (d.mNativeLayer != 0) { 1647 nativeScrollLayer(mNativeClass, 1648 d.mNativeLayer, d.mNativeLayerRect); 1649 } 1650 WebKitHitTest hit = performHitTest(d.mX, d.mY, d.mSlop, true); 1651 mWebViewClassic.mPrivateHandler.obtainMessage( 1652 WebViewClassic.HIT_TEST_RESULT, hit) 1653 .sendToTarget(); 1654 break; 1655 1656 case SET_USE_MOCK_DEVICE_ORIENTATION: 1657 setUseMockDeviceOrientation(); 1658 break; 1659 1660 case AUTOFILL_FORM: 1661 nativeAutoFillForm(mNativeClass, msg.arg1); 1662 mWebViewClassic.mPrivateHandler.obtainMessage( 1663 WebViewClassic.AUTOFILL_COMPLETE, null).sendToTarget(); 1664 break; 1665 1666 case EXECUTE_JS: 1667 if (msg.obj instanceof String) { 1668 if (DebugFlags.WEB_VIEW_CORE) { 1669 Log.d(LOGTAG, "Executing JS : " + msg.obj); 1670 } 1671 mBrowserFrame.stringByEvaluatingJavaScriptFromString( 1672 (String) msg.obj); 1673 } 1674 break; 1675 case SCROLL_LAYER: 1676 int nativeLayer = msg.arg1; 1677 Rect rect = (Rect) msg.obj; 1678 nativeScrollLayer(mNativeClass, nativeLayer, 1679 rect); 1680 break; 1681 1682 case DELETE_TEXT: { 1683 int[] handles = (int[]) msg.obj; 1684 nativeDeleteText(mNativeClass, handles[0], 1685 handles[1], handles[2], handles[3]); 1686 break; 1687 } 1688 case COPY_TEXT: { 1689 int[] handles = (int[]) msg.obj; 1690 String copiedText = nativeGetText(mNativeClass, 1691 handles[0], handles[1], handles[2], 1692 handles[3]); 1693 if (copiedText != null) { 1694 mWebViewClassic.mPrivateHandler.obtainMessage( 1695 WebViewClassic.COPY_TO_CLIPBOARD, copiedText) 1696 .sendToTarget(); 1697 } 1698 break; 1699 } 1700 case INSERT_TEXT: 1701 nativeInsertText(mNativeClass, (String) msg.obj); 1702 break; 1703 case SELECT_TEXT: { 1704 int[] args = (int[]) msg.obj; 1705 if (args == null) { 1706 nativeClearTextSelection(mNativeClass); 1707 } else { 1708 nativeSelectText(mNativeClass, args[0], 1709 args[1], args[2], args[3]); 1710 } 1711 break; 1712 } 1713 case SELECT_WORD_AT: { 1714 int x = msg.arg1; 1715 int y = msg.arg2; 1716 if (!nativeSelectWordAt(mNativeClass, x, y)) { 1717 mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.SHOW_CARET_HANDLE) 1718 .sendToTarget(); 1719 } 1720 break; 1721 } 1722 case SELECT_ALL: 1723 nativeSelectAll(mNativeClass); 1724 break; 1725 case FIND_ALL: { 1726 FindAllRequest request = (FindAllRequest)msg.obj; 1727 if (request != null) { 1728 int matchCount = nativeFindAll(mNativeClass, request.mSearchText); 1729 int matchIndex = nativeFindNext(mNativeClass, true); 1730 synchronized (request) { 1731 request.mMatchCount = matchCount; 1732 request.mMatchIndex = matchIndex; 1733 request.notify(); 1734 } 1735 } else { 1736 nativeFindAll(mNativeClass, null); 1737 } 1738 Message.obtain(mWebViewClassic.mPrivateHandler, 1739 WebViewClassic.UPDATE_MATCH_COUNT, request).sendToTarget(); 1740 break; 1741 } 1742 case FIND_NEXT: { 1743 FindAllRequest request = (FindAllRequest)msg.obj; 1744 int matchIndex = nativeFindNext(mNativeClass, msg.arg1 != 0); 1745 synchronized (request) { 1746 request.mMatchIndex = matchIndex; 1747 } 1748 Message.obtain(mWebViewClassic.mPrivateHandler, 1749 WebViewClassic.UPDATE_MATCH_COUNT, request).sendToTarget(); 1750 break; 1751 } 1752 case SET_INITIAL_FOCUS: 1753 nativeSetInitialFocus(mNativeClass, msg.arg1); 1754 break; 1755 } 1756 } 1757 }; 1758 // Take all queued messages and resend them to the new handler. 1759 synchronized (this) { 1760 int size = mMessages.size(); 1761 for (int i = 0; i < size; i++) { 1762 mHandler.sendMessage(mMessages.get(i)); 1763 } 1764 mMessages = null; 1765 } 1766 } 1767 1768 @Override 1769 public Looper getWebKitLooper() { 1770 return mHandler.getLooper(); 1771 } 1772 1773 @Override 1774 public boolean dispatchWebKitEvent(MotionEvent event, int eventType, int flags) { 1775 if (mNativeClass == 0) { 1776 return false; 1777 } 1778 switch (eventType) { 1779 case WebViewInputDispatcher.EVENT_TYPE_CLICK: 1780 return nativeMouseClick(mNativeClass); 1781 1782 case WebViewInputDispatcher.EVENT_TYPE_TOUCH: { 1783 int count = event.getPointerCount(); 1784 int[] idArray = new int[count]; 1785 int[] xArray = new int[count]; 1786 int[] yArray = new int[count]; 1787 for (int i = 0; i < count; i++) { 1788 idArray[i] = event.getPointerId(i); 1789 xArray[i] = (int) event.getX(i); 1790 yArray[i] = (int) event.getY(i); 1791 } 1792 return nativeHandleTouchEvent(mNativeClass, 1793 event.getActionMasked(), 1794 idArray, xArray, yArray, count, 1795 event.getActionIndex(), event.getMetaState()); 1796 } 1797 1798 default: 1799 return false; 1800 } 1801 } 1802 1803 /** 1804 * Send a message internally to the queue or to the handler 1805 */ 1806 private synchronized void sendMessage(Message msg) { 1807 if (mBlockMessages) { 1808 return; 1809 } 1810 if (mMessages != null) { 1811 mMessages.add(msg); 1812 } else { 1813 mHandler.sendMessage(msg); 1814 } 1815 } 1816 1817 private synchronized void removeMessages(int what) { 1818 if (mBlockMessages) { 1819 return; 1820 } 1821 if (what == EventHub.WEBKIT_DRAW) { 1822 mDrawIsScheduled = false; 1823 } 1824 if (mMessages != null) { 1825 Throwable throwable = new Throwable( 1826 "EventHub.removeMessages(int what = " + what + ") is not supported " + 1827 "before the WebViewCore is set up."); 1828 Log.w(LOGTAG, Log.getStackTraceString(throwable)); 1829 } else { 1830 mHandler.removeMessages(what); 1831 } 1832 } 1833 1834 private synchronized void sendMessageDelayed(Message msg, long delay) { 1835 if (mBlockMessages) { 1836 return; 1837 } 1838 mHandler.sendMessageDelayed(msg, delay); 1839 } 1840 1841 /** 1842 * Send a message internally to the front of the queue. 1843 */ 1844 private synchronized void sendMessageAtFrontOfQueue(Message msg) { 1845 if (mBlockMessages) { 1846 return; 1847 } 1848 if (mMessages != null) { 1849 mMessages.add(0, msg); 1850 } else { 1851 mHandler.sendMessageAtFrontOfQueue(msg); 1852 } 1853 } 1854 1855 /** 1856 * Remove all the messages. 1857 */ 1858 private synchronized void removeMessages() { 1859 // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed 1860 mDrawIsScheduled = false; 1861 mSplitPictureIsScheduled = false; 1862 if (mMessages != null) { 1863 mMessages.clear(); 1864 } else { 1865 mHandler.removeCallbacksAndMessages(null); 1866 } 1867 } 1868 1869 /** 1870 * Block sending messages to the EventHub. 1871 */ 1872 private synchronized void blockMessages() { 1873 mBlockMessages = true; 1874 } 1875 } 1876 1877 //------------------------------------------------------------------------- 1878 // Methods called by host activity (in the same thread) 1879 //------------------------------------------------------------------------- 1880 1881 void stopLoading() { 1882 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading"); 1883 if (mBrowserFrame != null) { 1884 mBrowserFrame.stopLoading(); 1885 } 1886 } 1887 1888 //------------------------------------------------------------------------- 1889 // Methods called by WebView 1890 // If it refers to local variable, it needs synchronized(). 1891 // If it needs WebCore, it has to send message. 1892 //------------------------------------------------------------------------- 1893 1894 /** 1895 * @hide 1896 */ 1897 public void sendMessage(Message msg) { 1898 mEventHub.sendMessage(msg); 1899 } 1900 1901 void sendMessages(ArrayList<Message> messages) { 1902 synchronized (mEventHub) { 1903 for (int i = 0; i < messages.size(); i++) { 1904 mEventHub.sendMessage(messages.get(i)); 1905 } 1906 } 1907 } 1908 1909 void sendMessage(int what) { 1910 mEventHub.sendMessage(Message.obtain(null, what)); 1911 } 1912 1913 void sendMessageAtFrontOfQueue(int what, int arg1, int arg2, Object obj) { 1914 mEventHub.sendMessageAtFrontOfQueue(Message.obtain( 1915 null, what, arg1, arg2, obj)); 1916 } 1917 1918 void sendMessage(int what, Object obj) { 1919 mEventHub.sendMessage(Message.obtain(null, what, obj)); 1920 } 1921 1922 void sendMessage(int what, int arg1) { 1923 // just ignore the second argument (make it 0) 1924 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0)); 1925 } 1926 1927 void sendMessage(int what, int arg1, int arg2) { 1928 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2)); 1929 } 1930 1931 void sendMessage(int what, int arg1, Object obj) { 1932 // just ignore the second argument (make it 0) 1933 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj)); 1934 } 1935 1936 void sendMessage(int what, int arg1, int arg2, Object obj) { 1937 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj)); 1938 } 1939 1940 void sendMessageAtFrontOfQueue(int what, Object obj) { 1941 mEventHub.sendMessageAtFrontOfQueue(Message.obtain( 1942 null, what, obj)); 1943 } 1944 1945 void sendMessageDelayed(int what, Object obj, long delay) { 1946 mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay); 1947 } 1948 1949 void removeMessages(int what) { 1950 mEventHub.removeMessages(what); 1951 } 1952 1953 void removeMessages() { 1954 mEventHub.removeMessages(); 1955 } 1956 1957 /** 1958 * Sends a DESTROY message to WebCore. 1959 * Called from UI thread. 1960 */ 1961 void destroy() { 1962 synchronized (mEventHub) { 1963 // send DESTROY to front of queue 1964 // PAUSE/RESUME timers will still be processed even if they get handled later 1965 mEventHub.mDestroying = true; 1966 mEventHub.sendMessageAtFrontOfQueue( 1967 Message.obtain(null, EventHub.DESTROY)); 1968 mEventHub.blockMessages(); 1969 } 1970 } 1971 1972 //------------------------------------------------------------------------- 1973 // WebViewCore private methods 1974 //------------------------------------------------------------------------- 1975 1976 private WebKitHitTest performHitTest(int x, int y, int slop, boolean moveMouse) { 1977 WebKitHitTest hit = nativeHitTest(mNativeClass, x, y, slop, moveMouse); 1978 hit.mHitTestX = x; 1979 hit.mHitTestY = y; 1980 hit.mHitTestSlop = slop; 1981 hit.mHitTestMovedMouse = moveMouse; 1982 return hit; 1983 } 1984 1985 private void clearCache(boolean includeDiskFiles) { 1986 mBrowserFrame.clearCache(); 1987 if (includeDiskFiles) { 1988 CacheManager.removeAllCacheFiles(); 1989 } 1990 } 1991 1992 private void loadUrl(String url, Map<String, String> extraHeaders) { 1993 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url); 1994 mBrowserFrame.loadUrl(url, extraHeaders); 1995 } 1996 1997 private String saveWebArchive(String filename, boolean autoname) { 1998 if (DebugFlags.WEB_VIEW_CORE) { 1999 Log.v(LOGTAG, " CORE saveWebArchive " + filename + " " + autoname); 2000 } 2001 return mBrowserFrame.saveWebArchive(filename, autoname); 2002 } 2003 2004 private void key(KeyEvent evt, int canTakeFocusDirection, boolean isDown) { 2005 if (DebugFlags.WEB_VIEW_CORE) { 2006 Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", " 2007 + evt); 2008 } 2009 mChromeCanFocusDirection = canTakeFocusDirection; 2010 int keyCode = evt.getKeyCode(); 2011 int unicodeChar = evt.getUnicodeChar(); 2012 2013 if (keyCode == KeyEvent.KEYCODE_UNKNOWN && evt.getCharacters() != null 2014 && evt.getCharacters().length() > 0) { 2015 // we should only receive individual complex characters 2016 unicodeChar = evt.getCharacters().codePointAt(0); 2017 } 2018 2019 boolean handled = nativeKey(mNativeClass, keyCode, unicodeChar, evt.getRepeatCount(), 2020 evt.isShiftPressed(), evt.isAltPressed(), 2021 evt.isSymPressed(), isDown); 2022 mChromeCanFocusDirection = 0; 2023 if (!handled && keyCode != KeyEvent.KEYCODE_ENTER) { 2024 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP 2025 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { 2026 if (canTakeFocusDirection != 0 && isDown) { 2027 Message m = mWebViewClassic.mPrivateHandler.obtainMessage( 2028 WebViewClassic.TAKE_FOCUS); 2029 m.arg1 = canTakeFocusDirection; 2030 m.sendToTarget(); 2031 } 2032 return; 2033 } 2034 // bubble up the event handling 2035 // but do not bubble up the ENTER key, which would open the search 2036 // bar without any text. 2037 mCallbackProxy.onUnhandledKeyEvent(evt); 2038 } 2039 } 2040 2041 private void keyPress(int unicodeChar) { 2042 nativeKey(mNativeClass, 0, unicodeChar, 0, false, false, false, true); 2043 nativeKey(mNativeClass, 0, unicodeChar, 0, false, false, false, false); 2044 } 2045 2046 // These values are used to avoid requesting a layout based on old values 2047 private int mCurrentViewWidth = 0; 2048 private int mCurrentViewHeight = 0; 2049 private float mCurrentViewScale = 1.0f; 2050 2051 // notify webkit that our virtual view size changed size (after inv-zoom) 2052 private void viewSizeChanged(WebViewClassic.ViewSizeData data) { 2053 int w = data.mWidth; 2054 int h = data.mHeight; 2055 int textwrapWidth = data.mTextWrapWidth; 2056 float scale = data.mScale; 2057 if (DebugFlags.WEB_VIEW_CORE) { 2058 Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h 2059 + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale); 2060 } 2061 if (w == 0) { 2062 Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); 2063 return; 2064 } 2065 int width = calculateWindowWidth(w); 2066 int height = h; 2067 if (width != w) { 2068 float heightWidthRatio = data.mHeightWidthRatio; 2069 float ratio = (heightWidthRatio > 0) ? heightWidthRatio : (float) h / w; 2070 height = Math.round(ratio * width); 2071 } 2072 int screenHeight = data.mActualViewHeight > 0 ? data.mActualViewHeight : h; 2073 nativeSetSize(mNativeClass, width, height, textwrapWidth, scale, 2074 w, screenHeight, data.mAnchorX, data.mAnchorY, data.mIgnoreHeight); 2075 // Remember the current width and height 2076 boolean needInvalidate = (mCurrentViewWidth == 0); 2077 mCurrentViewWidth = w; 2078 mCurrentViewHeight = h; 2079 mCurrentViewScale = scale; 2080 if (needInvalidate) { 2081 // ensure {@link #webkitDraw} is called as we were blocking in 2082 // {@link #contentDraw} when mCurrentViewWidth is 0 2083 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged"); 2084 contentDraw(); 2085 } 2086 } 2087 2088 // Calculate width to be used in webkit window. 2089 private int calculateWindowWidth(int viewWidth) { 2090 int width = viewWidth; 2091 if (mSettings.getUseWideViewPort()) { 2092 if (mViewportWidth == -1) { 2093 // Fixed viewport width. 2094 width = WebViewClassic.DEFAULT_VIEWPORT_WIDTH; 2095 } else if (mViewportWidth > 0) { 2096 // Use website specified or desired fixed viewport width. 2097 width = mViewportWidth; 2098 } else { 2099 // For mobile web site. 2100 width = Math.round(mWebViewClassic.getViewWidth() / 2101 mWebViewClassic.getDefaultZoomScale()); 2102 } 2103 } 2104 return width; 2105 } 2106 2107 // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize 2108 // callbacks. Computes the sum of database quota for all origins. 2109 private long getUsedQuota() { 2110 WebStorage webStorage = WebStorage.getInstance(); 2111 Collection<WebStorage.Origin> origins = webStorage.getOriginsSync(); 2112 2113 if (origins == null) { 2114 return 0; 2115 } 2116 long usedQuota = 0; 2117 for (WebStorage.Origin website : origins) { 2118 usedQuota += website.getQuota(); 2119 } 2120 return usedQuota; 2121 } 2122 2123 // called from UI thread 2124 void splitContent(int content) { 2125 if (!mSplitPictureIsScheduled) { 2126 mSplitPictureIsScheduled = true; 2127 sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0); 2128 } 2129 } 2130 2131 // Used to avoid posting more than one draw message. 2132 private boolean mDrawIsScheduled; 2133 2134 // Used to avoid posting more than one split picture message. 2135 private boolean mSplitPictureIsScheduled; 2136 2137 // Used to suspend drawing. 2138 private boolean mDrawIsPaused; 2139 2140 // mInitialViewState is set by didFirstLayout() and then reset in the 2141 // next webkitDraw after passing the state to the UI thread. 2142 private ViewState mInitialViewState = null; 2143 private boolean mFirstLayoutForNonStandardLoad; 2144 2145 static class ViewState { 2146 float mMinScale; 2147 float mMaxScale; 2148 float mViewScale; 2149 float mTextWrapScale; 2150 float mDefaultScale; 2151 int mScrollX; 2152 int mScrollY; 2153 boolean mMobileSite; 2154 boolean mIsRestored; 2155 boolean mShouldStartScrolledRight; 2156 } 2157 2158 static class DrawData { 2159 DrawData() { 2160 mBaseLayer = 0; 2161 mInvalRegion = new Region(); 2162 mContentSize = new Point(); 2163 } 2164 int mBaseLayer; 2165 Region mInvalRegion; 2166 // view size that was used by webkit during the most recent layout 2167 Point mViewSize; 2168 Point mContentSize; 2169 int mMinPrefWidth; 2170 // only non-null if it is for the first picture set after the first layout 2171 ViewState mViewState; 2172 boolean mFirstLayoutForNonStandardLoad; 2173 boolean mFocusSizeChanged; 2174 } 2175 2176 DrawData mLastDrawData = null; 2177 2178 private Boolean m_skipDrawFlag = false; 2179 private boolean m_drawWasSkipped = false; 2180 2181 void pauseWebKitDraw() { 2182 synchronized (m_skipDrawFlag) { 2183 if (!m_skipDrawFlag) { 2184 m_skipDrawFlag = true; 2185 } 2186 } 2187 } 2188 2189 void resumeWebKitDraw() { 2190 synchronized (m_skipDrawFlag) { 2191 if (m_skipDrawFlag && m_drawWasSkipped) { 2192 // a draw was dropped, send a retry 2193 m_drawWasSkipped = false; 2194 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 2195 } 2196 m_skipDrawFlag = false; 2197 } 2198 } 2199 2200 private void webkitDraw() { 2201 synchronized (m_skipDrawFlag) { 2202 if (m_skipDrawFlag) { 2203 m_drawWasSkipped = true; 2204 return; 2205 } 2206 } 2207 2208 mDrawIsScheduled = false; 2209 DrawData draw = new DrawData(); 2210 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); 2211 draw.mBaseLayer = nativeRecordContent(mNativeClass, draw.mInvalRegion, 2212 draw.mContentSize); 2213 if (draw.mBaseLayer == 0) { 2214 if (mWebViewClassic != null && !mWebViewClassic.isPaused()) { 2215 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message"); 2216 mEventHub.sendMessageDelayed(Message.obtain(null, EventHub.WEBKIT_DRAW), 10); 2217 } else { 2218 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, webview paused"); 2219 } 2220 return; 2221 } 2222 mLastDrawData = draw; 2223 webkitDraw(draw); 2224 } 2225 2226 private void webkitDraw(DrawData draw) { 2227 if (mWebViewClassic != null) { 2228 draw.mFocusSizeChanged = nativeFocusBoundsChanged(mNativeClass); 2229 draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight); 2230 if (mSettings.getUseWideViewPort()) { 2231 draw.mMinPrefWidth = Math.max( 2232 mViewportWidth == -1 ? WebViewClassic.DEFAULT_VIEWPORT_WIDTH 2233 : (mViewportWidth == 0 ? mCurrentViewWidth 2234 : mViewportWidth), 2235 nativeGetContentMinPrefWidth(mNativeClass)); 2236 } 2237 if (mInitialViewState != null) { 2238 draw.mViewState = mInitialViewState; 2239 mInitialViewState = null; 2240 } 2241 if (mFirstLayoutForNonStandardLoad) { 2242 draw.mFirstLayoutForNonStandardLoad = true; 2243 mFirstLayoutForNonStandardLoad = false; 2244 } 2245 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); 2246 pauseWebKitDraw(); 2247 Message.obtain(mWebViewClassic.mPrivateHandler, 2248 WebViewClassic.NEW_PICTURE_MSG_ID, draw).sendToTarget(); 2249 } 2250 } 2251 2252 static void reducePriority() { 2253 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 2254 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 2255 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 2256 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 2257 .obtainMessage(WebCoreThread.REDUCE_PRIORITY)); 2258 } 2259 2260 static void resumePriority() { 2261 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 2262 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 2263 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 2264 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 2265 .obtainMessage(WebCoreThread.RESUME_PRIORITY)); 2266 } 2267 2268 static void sendStaticMessage(int messageType, Object argument) { 2269 if (sWebCoreHandler == null) 2270 return; 2271 2272 sWebCoreHandler.sendMessage(sWebCoreHandler.obtainMessage(messageType, argument)); 2273 } 2274 2275 static void pauseUpdatePicture(WebViewCore core) { 2276 // Note: there is one possible failure mode. If pauseUpdatePicture() is 2277 // called from UI thread while WEBKIT_DRAW is just pulled out of the 2278 // queue in WebCore thread to be executed. Then update won't be blocked. 2279 if (core != null) { 2280 if (!core.getSettings().enableSmoothTransition()) return; 2281 2282 synchronized (core) { 2283 if (core.mNativeClass == 0) { 2284 Log.w(LOGTAG, "Cannot pauseUpdatePicture, core destroyed or not initialized!"); 2285 return; 2286 } 2287 core.nativeSetIsPaused(core.mNativeClass, true); 2288 core.mDrawIsPaused = true; 2289 } 2290 } 2291 2292 } 2293 2294 static void resumeUpdatePicture(WebViewCore core) { 2295 if (core != null) { 2296 // if mDrawIsPaused is true, ignore the setting, continue to resume 2297 if (!core.mDrawIsPaused) 2298 return; 2299 2300 synchronized (core) { 2301 if (core.mNativeClass == 0) { 2302 Log.w(LOGTAG, "Cannot resumeUpdatePicture, core destroyed!"); 2303 return; 2304 } 2305 core.nativeSetIsPaused(core.mNativeClass, false); 2306 core.mDrawIsPaused = false; 2307 // always redraw on resume to reenable gif animations 2308 core.mDrawIsScheduled = false; 2309 } 2310 } 2311 } 2312 2313 static boolean isUpdatePicturePaused(WebViewCore core) { 2314 return core != null ? core.mDrawIsPaused : false; 2315 } 2316 2317 ////////////////////////////////////////////////////////////////////////// 2318 2319 private void restoreState(int index) { 2320 WebBackForwardList list = mCallbackProxy.getBackForwardList(); 2321 int size = list.getSize(); 2322 for (int i = 0; i < size; i++) { 2323 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); 2324 } 2325 mBrowserFrame.mLoadInitFromJava = true; 2326 list.restoreIndex(mBrowserFrame.mNativeFrame, index); 2327 mBrowserFrame.mLoadInitFromJava = false; 2328 } 2329 2330 //------------------------------------------------------------------------- 2331 // Implement abstract methods in WebViewCore, native WebKit callback part 2332 //------------------------------------------------------------------------- 2333 2334 // called from JNI or WebView thread 2335 /* package */ void contentDraw() { 2336 synchronized (this) { 2337 if (mWebViewClassic == null || mBrowserFrame == null) { 2338 // We were destroyed 2339 return; 2340 } 2341 // don't update the Picture until we have an initial width and finish 2342 // the first layout 2343 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { 2344 return; 2345 } 2346 // only fire an event if this is our first request 2347 if (mDrawIsScheduled) return; 2348 mDrawIsScheduled = true; 2349 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 2350 } 2351 } 2352 2353 // called by JNI 2354 private void contentScrollTo(int x, int y, boolean animate, 2355 boolean onlyIfImeIsShowing) { 2356 if (!mBrowserFrame.firstLayoutDone()) { 2357 /* 2358 * WebKit restore state will be called before didFirstLayout(), 2359 * remember the position as it has to be applied after restoring 2360 * zoom factor which is controlled by screenWidth. 2361 */ 2362 mRestoredX = x; 2363 mRestoredY = y; 2364 return; 2365 } 2366 if (mWebViewClassic != null) { 2367 Message msg = Message.obtain(mWebViewClassic.mPrivateHandler, 2368 WebViewClassic.SCROLL_TO_MSG_ID, animate ? 1 : 0, 2369 onlyIfImeIsShowing ? 1 : 0, new Point(x, y)); 2370 if (mDrawIsScheduled) { 2371 mEventHub.sendMessage(Message.obtain(null, 2372 EventHub.MESSAGE_RELAY, msg)); 2373 } else { 2374 msg.sendToTarget(); 2375 } 2376 } 2377 } 2378 2379 // called by JNI 2380 private void sendNotifyProgressFinished() { 2381 contentDraw(); 2382 } 2383 2384 /* Called by JNI. The coordinates are in doc coordinates, so they need to 2385 be scaled before they can be used by the view system, which happens 2386 in WebView since it (and its thread) know the current scale factor. 2387 */ 2388 private void sendViewInvalidate(int left, int top, int right, int bottom) { 2389 if (mWebViewClassic != null) { 2390 Message.obtain(mWebViewClassic.mPrivateHandler, 2391 WebViewClassic.INVAL_RECT_MSG_ID, 2392 new Rect(left, top, right, bottom)).sendToTarget(); 2393 } 2394 } 2395 2396 private static boolean mRepaintScheduled = false; 2397 2398 /* 2399 * Called by the WebView thread 2400 */ 2401 /* package */ void signalRepaintDone() { 2402 mRepaintScheduled = false; 2403 } 2404 2405 // Gets the WebViewClassic corresponding to this WebViewCore. Note that the 2406 // WebViewClassic object must only be used on the UI thread. 2407 /* package */ WebViewClassic getWebViewClassic() { 2408 return mWebViewClassic; 2409 } 2410 2411 // Called by JNI 2412 private WebView getWebView() { 2413 return mWebViewClassic.getWebView(); 2414 } 2415 2416 // Called by JNI 2417 private void sendPluginDrawMsg() { 2418 sendMessage(EventHub.PLUGIN_SURFACE_READY); 2419 } 2420 2421 private native void setViewportSettingsFromNative(int nativeClass); 2422 2423 // called by JNI 2424 private void didFirstLayout(boolean standardLoad) { 2425 if (DebugFlags.WEB_VIEW_CORE) { 2426 Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad); 2427 } 2428 2429 mBrowserFrame.didFirstLayout(); 2430 2431 if (mWebViewClassic == null) return; 2432 2433 boolean updateViewState = standardLoad || mIsRestored; 2434 setupViewport(updateViewState); 2435 // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will 2436 // be called after the WebView updates its state. If updateRestoreState 2437 // is false, start to draw now as it is ready. 2438 if (!updateViewState) { 2439 mWebViewClassic.mViewManager.postReadyToDrawAll(); 2440 } 2441 2442 // remove the touch highlight when moving to a new page 2443 mWebViewClassic.mPrivateHandler.sendEmptyMessage( 2444 WebViewClassic.HIT_TEST_RESULT); 2445 2446 // reset the scroll position, the restored offset and scales 2447 mRestoredX = mRestoredY = 0; 2448 mIsRestored = false; 2449 mRestoredScale = mRestoredTextWrapScale = 0; 2450 } 2451 2452 // called by JNI 2453 private void updateViewport() { 2454 // Update viewport asap to make sure we get correct one. 2455 setupViewport(true); 2456 } 2457 2458 private void setupViewport(boolean updateViewState) { 2459 if (mWebViewClassic == null || mSettings == null) { 2460 // We've been destroyed or are being destroyed, return early 2461 return; 2462 } 2463 // set the viewport settings from WebKit 2464 setViewportSettingsFromNative(mNativeClass); 2465 2466 // clamp initial scale 2467 if (mViewportInitialScale > 0) { 2468 if (mViewportMinimumScale > 0) { 2469 mViewportInitialScale = Math.max(mViewportInitialScale, 2470 mViewportMinimumScale); 2471 } 2472 if (mViewportMaximumScale > 0) { 2473 mViewportInitialScale = Math.min(mViewportInitialScale, 2474 mViewportMaximumScale); 2475 } 2476 } 2477 2478 if (mSettings.forceUserScalable()) { 2479 mViewportUserScalable = true; 2480 if (mViewportInitialScale > 0) { 2481 if (mViewportMinimumScale > 0) { 2482 mViewportMinimumScale = Math.min(mViewportMinimumScale, 2483 mViewportInitialScale / 2); 2484 } 2485 if (mViewportMaximumScale > 0) { 2486 mViewportMaximumScale = Math.max(mViewportMaximumScale, 2487 mViewportInitialScale * 2); 2488 } 2489 } else { 2490 if (mViewportMinimumScale > 0) { 2491 mViewportMinimumScale = Math.min(mViewportMinimumScale, 50); 2492 } 2493 if (mViewportMaximumScale > 0) { 2494 mViewportMaximumScale = Math.max(mViewportMaximumScale, 200); 2495 } 2496 } 2497 } 2498 2499 // adjust the default scale to match the densityDpi 2500 float adjust = 1.0f; 2501 if (mViewportDensityDpi == -1) { 2502 adjust = mContext.getResources().getDisplayMetrics().density; 2503 } else if (mViewportDensityDpi > 0) { 2504 adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi 2505 / mViewportDensityDpi; 2506 } 2507 if (adjust != mWebViewClassic.getDefaultZoomScale()) { 2508 Message.obtain(mWebViewClassic.mPrivateHandler, 2509 WebViewClassic.UPDATE_ZOOM_DENSITY, adjust).sendToTarget(); 2510 } 2511 int defaultScale = (int) (adjust * 100); 2512 2513 if (mViewportInitialScale > 0) { 2514 mViewportInitialScale *= adjust; 2515 } 2516 if (mViewportMinimumScale > 0) { 2517 mViewportMinimumScale *= adjust; 2518 } 2519 if (mViewportMaximumScale > 0) { 2520 mViewportMaximumScale *= adjust; 2521 } 2522 2523 // infer the values if they are not defined. 2524 if (mViewportWidth == 0) { 2525 if (mViewportInitialScale == 0) { 2526 mViewportInitialScale = defaultScale; 2527 } 2528 } 2529 if (mViewportUserScalable == false) { 2530 mViewportInitialScale = defaultScale; 2531 mViewportMinimumScale = defaultScale; 2532 mViewportMaximumScale = defaultScale; 2533 } 2534 if (mViewportMinimumScale > mViewportInitialScale 2535 && mViewportInitialScale != 0) { 2536 mViewportMinimumScale = mViewportInitialScale; 2537 } 2538 if (mViewportMaximumScale > 0 2539 && mViewportMaximumScale < mViewportInitialScale) { 2540 mViewportMaximumScale = mViewportInitialScale; 2541 } 2542 if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) { 2543 mViewportWidth = 0; 2544 } 2545 2546 // if mViewportWidth is 0, it means device-width, always update. 2547 if (mViewportWidth != 0 && !updateViewState) { 2548 // For non standard load, since updateViewState will be false. 2549 mFirstLayoutForNonStandardLoad = true; 2550 ViewState viewState = new ViewState(); 2551 viewState.mMinScale = mViewportMinimumScale / 100.0f; 2552 viewState.mMaxScale = mViewportMaximumScale / 100.0f; 2553 viewState.mDefaultScale = adjust; 2554 // as mViewportWidth is not 0, it is not mobile site. 2555 viewState.mMobileSite = false; 2556 // for non-mobile site, we don't need minPrefWidth, set it as 0 2557 viewState.mScrollX = 0; 2558 viewState.mShouldStartScrolledRight = false; 2559 Message.obtain(mWebViewClassic.mPrivateHandler, 2560 WebViewClassic.UPDATE_ZOOM_RANGE, viewState).sendToTarget(); 2561 return; 2562 } 2563 2564 // now notify webview 2565 // webViewWidth refers to the width in the view system 2566 int webViewWidth; 2567 // viewportWidth refers to the width in the document system 2568 int viewportWidth = mCurrentViewWidth; 2569 if (viewportWidth == 0) { 2570 // this may happen when WebView just starts. This is not perfect as 2571 // we call WebView method from WebCore thread. But not perfect 2572 // reference is better than no reference. 2573 webViewWidth = mWebViewClassic.getViewWidth(); 2574 viewportWidth = (int) (webViewWidth / adjust); 2575 if (viewportWidth == 0) { 2576 if (DebugFlags.WEB_VIEW_CORE) { 2577 Log.v(LOGTAG, "Can't get the viewWidth yet"); 2578 } 2579 } 2580 } else { 2581 webViewWidth = Math.round(viewportWidth * mCurrentViewScale); 2582 } 2583 mInitialViewState = new ViewState(); 2584 mInitialViewState.mMinScale = mViewportMinimumScale / 100.0f; 2585 mInitialViewState.mMaxScale = mViewportMaximumScale / 100.0f; 2586 mInitialViewState.mDefaultScale = adjust; 2587 mInitialViewState.mScrollX = mRestoredX; 2588 mInitialViewState.mScrollY = mRestoredY; 2589 mInitialViewState.mShouldStartScrolledRight = (mRestoredX == 0) 2590 && (mRestoredY == 0) 2591 && (mBrowserFrame != null) 2592 && mBrowserFrame.getShouldStartScrolledRight(); 2593 2594 mInitialViewState.mMobileSite = (0 == mViewportWidth); 2595 if (mIsRestored) { 2596 mInitialViewState.mIsRestored = true; 2597 mInitialViewState.mViewScale = mRestoredScale; 2598 if (mRestoredTextWrapScale > 0) { 2599 mInitialViewState.mTextWrapScale = mRestoredTextWrapScale; 2600 } else { 2601 mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale; 2602 } 2603 } else { 2604 if (mViewportInitialScale > 0) { 2605 mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale = 2606 mViewportInitialScale / 100.0f; 2607 } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth && 2608 !getSettings().getUseFixedViewport()) { 2609 mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale = 2610 (float) webViewWidth / mViewportWidth; 2611 } else { 2612 mInitialViewState.mTextWrapScale = adjust; 2613 if (mSettings.getUseWideViewPort()) { 2614 // 0 will trigger WebView to turn on zoom overview mode 2615 mInitialViewState.mViewScale = 0; 2616 } else { 2617 mInitialViewState.mViewScale = adjust; 2618 } 2619 } 2620 } 2621 2622 if (mWebViewClassic.mHeightCanMeasure) { 2623 // Trick to ensure that the Picture has the exact height for the 2624 // content by forcing to layout with 0 height after the page is 2625 // ready, which is indicated by didFirstLayout. This is essential to 2626 // get rid of the white space in the GMail which uses WebView for 2627 // message view. 2628 mWebViewClassic.mLastHeightSent = 0; 2629 // Send a negative scale to indicate that WebCore should reuse 2630 // the current scale 2631 WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData(); 2632 data.mWidth = mWebViewClassic.mLastWidthSent; 2633 data.mHeight = 0; 2634 // if mHeightCanMeasure is true, getUseWideViewPort() can't be 2635 // true. It is safe to use mWidth for mTextWrapWidth. 2636 data.mTextWrapWidth = data.mWidth; 2637 data.mScale = -1.0f; 2638 data.mIgnoreHeight = false; 2639 data.mAnchorX = data.mAnchorY = 0; 2640 // send VIEW_SIZE_CHANGED to the front of the queue so that we can 2641 // avoid pushing the wrong picture to the WebView side. If there is 2642 // a VIEW_SIZE_CHANGED in the queue, probably from WebView side, 2643 // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED 2644 // in the queue, as mLastHeightSent has been updated here, we may 2645 // miss the requestLayout in WebView side after the new picture. 2646 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2647 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 2648 EventHub.VIEW_SIZE_CHANGED, data)); 2649 } else { 2650 if (viewportWidth == 0) { 2651 // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView 2652 // to WebViewCore 2653 mWebViewClassic.mLastWidthSent = 0; 2654 } else { 2655 WebViewClassic.ViewSizeData data = new WebViewClassic.ViewSizeData(); 2656 // mViewScale as 0 means it is in zoom overview mode. So we don't 2657 // know the exact scale. If mRestoredScale is non-zero, use it; 2658 // otherwise just use mTextWrapScale as the initial scale. 2659 float tentativeScale = mInitialViewState.mViewScale; 2660 if (tentativeScale == 0) { 2661 // The following tries to figure out more correct view scale 2662 // and text wrap scale to be sent to webkit, by using some 2663 // knowledge from web settings and zoom manager. 2664 2665 // Calculated window width will be used to guess the scale 2666 // in zoom overview mode. 2667 tentativeScale = mInitialViewState.mTextWrapScale; 2668 int tentativeViewWidth = Math.round(webViewWidth / tentativeScale); 2669 int windowWidth = calculateWindowWidth(tentativeViewWidth); 2670 // In viewport setup time, since no content width is known, we assume 2671 // the windowWidth will be the content width, to get a more likely 2672 // zoom overview scale. 2673 data.mScale = (float) webViewWidth / windowWidth; 2674 if (!mSettings.getLoadWithOverviewMode()) { 2675 // If user choose non-overview mode. 2676 data.mScale = Math.max(data.mScale, tentativeScale); 2677 } 2678 if (mSettings.isNarrowColumnLayout()) { 2679 // In case of automatic text reflow in fixed view port mode. 2680 mInitialViewState.mTextWrapScale = 2681 mWebViewClassic.computeReadingLevelScale(data.mScale); 2682 } 2683 } else { 2684 // Scale is given such as when page is restored, use it. 2685 data.mScale = tentativeScale; 2686 } 2687 if (DebugFlags.WEB_VIEW_CORE) { 2688 Log.v(LOGTAG, "setupViewport" 2689 + " mRestoredScale=" + mRestoredScale 2690 + " mViewScale=" + mInitialViewState.mViewScale 2691 + " mTextWrapScale=" + mInitialViewState.mTextWrapScale 2692 + " data.mScale= " + data.mScale 2693 ); 2694 } 2695 data.mWidth = Math.round(webViewWidth / data.mScale); 2696 // We may get a call here when mCurrentViewHeight == 0 if webcore completes the 2697 // first layout before we sync our webview dimensions to it. In that case, we 2698 // request the real height of the webview. This is not a perfect solution as we 2699 // are calling a WebView method from the WebCore thread. But this is preferable 2700 // to syncing an incorrect height. 2701 data.mHeight = mCurrentViewHeight == 0 ? 2702 Math.round(mWebViewClassic.getViewHeight() / data.mScale) 2703 : Math.round((float) mCurrentViewHeight * data.mWidth / viewportWidth); 2704 data.mTextWrapWidth = Math.round(webViewWidth 2705 / mInitialViewState.mTextWrapScale); 2706 data.mIgnoreHeight = false; 2707 data.mAnchorX = data.mAnchorY = 0; 2708 // send VIEW_SIZE_CHANGED to the front of the queue so that we 2709 // can avoid pushing the wrong picture to the WebView side. 2710 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2711 // Let webkit know the scale and inner width/height immediately 2712 // in viewport setup time to avoid wrong information. 2713 viewSizeChanged(data); 2714 } 2715 } 2716 } 2717 2718 // called by JNI 2719 private void restoreScale(float scale, float textWrapScale) { 2720 if (mBrowserFrame.firstLayoutDone() == false) { 2721 mIsRestored = true; 2722 mRestoredScale = scale; 2723 if (mSettings.getUseWideViewPort()) { 2724 mRestoredTextWrapScale = textWrapScale; 2725 } 2726 } 2727 } 2728 2729 // called by JNI 2730 private void needTouchEvents(boolean need) { 2731 if (mWebViewClassic != null) { 2732 Message.obtain(mWebViewClassic.mPrivateHandler, 2733 WebViewClassic.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0) 2734 .sendToTarget(); 2735 } 2736 } 2737 2738 // called by JNI 2739 private void updateTextfield(int ptr, boolean changeToPassword, 2740 String text, int textGeneration) { 2741 if (mWebViewClassic != null) { 2742 Message msg = Message.obtain(mWebViewClassic.mPrivateHandler, 2743 WebViewClassic.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 2744 textGeneration, text); 2745 msg.getData().putBoolean("password", changeToPassword); 2746 msg.sendToTarget(); 2747 } 2748 } 2749 2750 // called by JNI 2751 private void updateTextSelection(int pointer, int start, int end, 2752 int textGeneration, int selectionPtr) { 2753 if (mWebViewClassic != null) { 2754 Message.obtain(mWebViewClassic.mPrivateHandler, 2755 WebViewClassic.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration, 2756 new TextSelectionData(start, end, selectionPtr)).sendToTarget(); 2757 } 2758 } 2759 2760 // called by JNI 2761 private void updateTextSizeAndScroll(int pointer, int width, int height, 2762 int scrollX, int scrollY) { 2763 if (mWebViewClassic != null) { 2764 Rect rect = new Rect(-scrollX, -scrollY, width - scrollX, 2765 height - scrollY); 2766 Message.obtain(mWebViewClassic.mPrivateHandler, 2767 WebViewClassic.EDIT_TEXT_SIZE_CHANGED, pointer, 0, rect) 2768 .sendToTarget(); 2769 } 2770 } 2771 2772 // called by JNI 2773 private void clearTextEntry() { 2774 if (mWebViewClassic == null) return; 2775 Message.obtain(mWebViewClassic.mPrivateHandler, 2776 WebViewClassic.CLEAR_TEXT_ENTRY).sendToTarget(); 2777 } 2778 2779 // called by JNI 2780 private void initEditField(int start, int end, int selectionPtr, 2781 TextFieldInitData initData) { 2782 if (mWebViewClassic == null) { 2783 return; 2784 } 2785 Message.obtain(mWebViewClassic.mPrivateHandler, 2786 WebViewClassic.INIT_EDIT_FIELD, initData).sendToTarget(); 2787 Message.obtain(mWebViewClassic.mPrivateHandler, 2788 WebViewClassic.UPDATE_TEXT_SELECTION_MSG_ID, 2789 initData.mFieldPointer, 0, 2790 new TextSelectionData(start, end, selectionPtr)) 2791 .sendToTarget(); 2792 } 2793 2794 private native void nativeRevealSelection(int nativeClass); 2795 private native String nativeRequestLabel(int nativeClass, int framePtr, 2796 int nodePtr); 2797 /** 2798 * Scroll the focused textfield to (xPercent, y) in document space 2799 */ 2800 private native void nativeScrollFocusedTextInput(int nativeClass, 2801 float xPercent, int y, Rect contentBounds); 2802 2803 // these must be in document space (i.e. not scaled/zoomed). 2804 private native void nativeSetScrollOffset(int nativeClass, 2805 boolean sendScrollEvent, int dx, int dy); 2806 2807 private native void nativeSetGlobalBounds(int nativeClass, int x, int y, 2808 int w, int h); 2809 2810 // called by JNI 2811 private void requestListBox(String[] array, int[] enabledArray, 2812 int[] selectedArray) { 2813 if (mWebViewClassic != null) { 2814 mWebViewClassic.requestListBox(array, enabledArray, selectedArray); 2815 } 2816 } 2817 2818 // called by JNI 2819 private void requestListBox(String[] array, int[] enabledArray, 2820 int selection) { 2821 if (mWebViewClassic != null) { 2822 mWebViewClassic.requestListBox(array, enabledArray, selection); 2823 } 2824 2825 } 2826 2827 // called by JNI 2828 private void requestKeyboard(boolean showKeyboard) { 2829 if (mWebViewClassic != null) { 2830 Message.obtain(mWebViewClassic.mPrivateHandler, 2831 WebViewClassic.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0) 2832 .sendToTarget(); 2833 } 2834 } 2835 2836 private void setWebTextViewAutoFillable(int queryId, String preview) { 2837 if (mWebViewClassic != null) { 2838 Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.SET_AUTOFILLABLE, 2839 new AutoFillData(queryId, preview)) 2840 .sendToTarget(); 2841 } 2842 } 2843 2844 Context getContext() { 2845 return mContext; 2846 } 2847 2848 // called by JNI 2849 private void keepScreenOn(boolean screenOn) { 2850 if (mWebViewClassic != null) { 2851 Message message = mWebViewClassic.mPrivateHandler.obtainMessage( 2852 WebViewClassic.SCREEN_ON); 2853 message.arg1 = screenOn ? 1 : 0; 2854 message.sendToTarget(); 2855 } 2856 } 2857 2858 // called by JNI 2859 private Class<?> getPluginClass(String libName, String clsName) { 2860 2861 if (mWebViewClassic == null) { 2862 return null; 2863 } 2864 2865 PluginManager pluginManager = PluginManager.getInstance(null); 2866 2867 String pkgName = pluginManager.getPluginsAPKName(libName); 2868 if (pkgName == null) { 2869 Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); 2870 return null; 2871 } 2872 2873 try { 2874 return pluginManager.getPluginClass(pkgName, clsName); 2875 } catch (NameNotFoundException e) { 2876 Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")"); 2877 } catch (ClassNotFoundException e) { 2878 Log.e(LOGTAG, "Unable to find plugin class (" + clsName + 2879 ") in the apk (" + pkgName + ")"); 2880 } 2881 2882 return null; 2883 } 2884 2885 // called by JNI. PluginWidget function to launch a full-screen view using a 2886 // View object provided by the plugin class. 2887 private void showFullScreenPlugin(ViewManager.ChildView childView, int orientation, int npp) { 2888 if (mWebViewClassic == null) { 2889 return; 2890 } 2891 2892 Message message = mWebViewClassic.mPrivateHandler.obtainMessage( 2893 WebViewClassic.SHOW_FULLSCREEN); 2894 message.obj = childView.mView; 2895 message.arg1 = orientation; 2896 message.arg2 = npp; 2897 message.sendToTarget(); 2898 } 2899 2900 // called by JNI 2901 private void hideFullScreenPlugin() { 2902 if (mWebViewClassic == null) { 2903 return; 2904 } 2905 mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.HIDE_FULLSCREEN) 2906 .sendToTarget(); 2907 } 2908 2909 private ViewManager.ChildView createSurface(View pluginView) { 2910 if (mWebViewClassic == null) { 2911 return null; 2912 } 2913 2914 if (pluginView == null) { 2915 Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy"); 2916 return null; 2917 } 2918 2919 // ensures the view system knows the view can redraw itself 2920 pluginView.setWillNotDraw(false); 2921 2922 if(pluginView instanceof SurfaceView) 2923 ((SurfaceView)pluginView).setZOrderOnTop(true); 2924 2925 ViewManager.ChildView view = mWebViewClassic.mViewManager.createView(); 2926 view.mView = pluginView; 2927 return view; 2928 } 2929 2930 // called by JNI. PluginWidget functions for creating an embedded View for 2931 // the surface drawing model. 2932 private ViewManager.ChildView addSurface(View pluginView, int x, int y, 2933 int width, int height) { 2934 ViewManager.ChildView view = createSurface(pluginView); 2935 view.attachView(x, y, width, height); 2936 return view; 2937 } 2938 2939 private void updateSurface(ViewManager.ChildView childView, int x, int y, 2940 int width, int height) { 2941 childView.attachView(x, y, width, height); 2942 } 2943 2944 private void destroySurface(ViewManager.ChildView childView) { 2945 childView.removeView(); 2946 } 2947 2948 // called by JNI 2949 static class ShowRectData { 2950 int mLeft; 2951 int mTop; 2952 int mWidth; 2953 int mHeight; 2954 int mContentWidth; 2955 int mContentHeight; 2956 float mXPercentInDoc; 2957 float mXPercentInView; 2958 float mYPercentInDoc; 2959 float mYPercentInView; 2960 } 2961 2962 private void showRect(int left, int top, int width, int height, 2963 int contentWidth, int contentHeight, float xPercentInDoc, 2964 float xPercentInView, float yPercentInDoc, float yPercentInView) { 2965 if (mWebViewClassic != null) { 2966 ShowRectData data = new ShowRectData(); 2967 data.mLeft = left; 2968 data.mTop = top; 2969 data.mWidth = width; 2970 data.mHeight = height; 2971 data.mContentWidth = contentWidth; 2972 data.mContentHeight = contentHeight; 2973 data.mXPercentInDoc = xPercentInDoc; 2974 data.mXPercentInView = xPercentInView; 2975 data.mYPercentInDoc = yPercentInDoc; 2976 data.mYPercentInView = yPercentInView; 2977 Message.obtain(mWebViewClassic.mPrivateHandler, WebViewClassic.SHOW_RECT_MSG_ID, 2978 data).sendToTarget(); 2979 } 2980 } 2981 2982 // called by JNI 2983 private void centerFitRect(int x, int y, int width, int height) { 2984 if (mWebViewClassic == null) { 2985 return; 2986 } 2987 mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.CENTER_FIT_RECT, 2988 new Rect(x, y, x + width, y + height)).sendToTarget(); 2989 } 2990 2991 // called by JNI 2992 private void setScrollbarModes(int hMode, int vMode) { 2993 if (mWebViewClassic == null) { 2994 return; 2995 } 2996 mWebViewClassic.mPrivateHandler.obtainMessage(WebViewClassic.SET_SCROLLBAR_MODES, 2997 hMode, vMode).sendToTarget(); 2998 } 2999 3000 // called by JNI 3001 private void selectAt(int x, int y) { 3002 // TODO: Figure out what to do with this (b/6111818) 3003 } 3004 3005 private void setUseMockDeviceOrientation() { 3006 mDeviceMotionAndOrientationManager.setUseMock(); 3007 } 3008 3009 public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha, 3010 boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) { 3011 mDeviceMotionAndOrientationManager.setMockOrientation(canProvideAlpha, alpha, 3012 canProvideBeta, beta, canProvideGamma, gamma); 3013 } 3014 3015 protected DeviceMotionService getDeviceMotionService() { 3016 if (mDeviceMotionService == null) { 3017 mDeviceMotionService = 3018 new DeviceMotionService(mDeviceMotionAndOrientationManager, mContext); 3019 } 3020 return mDeviceMotionService; 3021 } 3022 3023 protected DeviceOrientationService getDeviceOrientationService() { 3024 if (mDeviceOrientationService == null) { 3025 mDeviceOrientationService = 3026 new DeviceOrientationService(mDeviceMotionAndOrientationManager, mContext); 3027 } 3028 return mDeviceOrientationService; 3029 } 3030 3031 private native void nativeSetIsPaused(int nativeClass, boolean isPaused); 3032 private native void nativePause(int nativeClass); 3033 private native void nativeResume(int nativeClass); 3034 private native void nativeFreeMemory(int nativeClass); 3035 private native void nativeFullScreenPluginHidden(int nativeClass, int npp); 3036 private native void nativePluginSurfaceReady(int nativeClass); 3037 3038 private native WebKitHitTest nativeHitTest(int nativeClass, int x, int y, 3039 int slop, boolean moveMouse); 3040 3041 private native void nativeAutoFillForm(int nativeClass, int queryId); 3042 private native void nativeScrollLayer(int nativeClass, int layer, Rect rect); 3043 private native int nativeFindAll(int nativeClass, String text); 3044 private native int nativeFindNext(int nativeClass, boolean forward); 3045 3046 /** 3047 * Deletes editable text between two points. Note that the selection may 3048 * differ from the WebView's selection because the algorithms for selecting 3049 * text differs for non-LTR text. Any text that isn't editable will be 3050 * left unchanged. 3051 * @param nativeClass The pointer to the native class (mNativeClass) 3052 * @param startX The X position of the top-left selection point. 3053 * @param startY The Y position of the top-left selection point. 3054 * @param endX The X position of the bottom-right selection point. 3055 * @param endY The Y position of the bottom-right selection point. 3056 */ 3057 private native void nativeDeleteText(int nativeClass, 3058 int startX, int startY, int endX, int endY); 3059 /** 3060 * Inserts text at the current cursor position. If the currently-focused 3061 * node does not have a cursor position then this function does nothing. 3062 */ 3063 private native void nativeInsertText(int nativeClass, String text); 3064 /** 3065 * Gets the text between two selection points. Note that the selection 3066 * may differ from the WebView's selection because the algorithms for 3067 * selecting text differs for non-LTR text. 3068 * @param nativeClass The pointer to the native class (mNativeClass) 3069 * @param startX The X position of the top-left selection point. 3070 * @param startY The Y position of the top-left selection point. 3071 * @param endX The X position of the bottom-right selection point. 3072 * @param endY The Y position of the bottom-right selection point. 3073 */ 3074 private native String nativeGetText(int nativeClass, 3075 int startX, int startY, int endX, int endY); 3076 private native void nativeSelectText(int nativeClass, 3077 int startX, int startY, int endX, int endY); 3078 private native void nativeClearTextSelection(int nativeClass); 3079 private native boolean nativeSelectWordAt(int nativeClass, int x, int y); 3080 private native void nativeSelectAll(int nativeClass); 3081 private native void nativeSetInitialFocus(int nativeClass, int keyDirection); 3082 3083 private static native void nativeCertTrustChanged(); 3084} 3085