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