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