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