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