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