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