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