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