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