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