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