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