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