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