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