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