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