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