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