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