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