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