WebViewCore.java revision be768ee508183c7454064f5c8bb772b44634ce98
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 // In 1.0, WebView.loadDataWithBaseURL() could access local 1145 // asset files using 'file' scheme URLs as long as the data is 1146 // valid. Later versions of WebKit have tightened the 1147 // restriction around when pages can access such local URLs. 1148 // To maintain compatibility with 1.0, we register the scheme of 1149 // the baseUrl to be considered local, as long as it is not 1150 // http(s)/ftp(s)/about/javascript. 1151 String scheme = baseUrl.substring(0, i); 1152 if (!scheme.startsWith("http") && 1153 !scheme.startsWith("ftp") && 1154 !scheme.startsWith("about") && 1155 !scheme.startsWith("javascript")) { 1156 nativeRegisterURLSchemeAsLocal(scheme); 1157 } 1158 } 1159 } 1160 mBrowserFrame.loadData(baseUrl, 1161 loadParams.mData, 1162 loadParams.mMimeType, 1163 loadParams.mEncoding, 1164 loadParams.mHistoryUrl); 1165 break; 1166 1167 case STOP_LOADING: 1168 // If the WebCore has committed the load, but not 1169 // finished the first layout yet, we need to set 1170 // first layout done to trigger the interpreted side sync 1171 // up with native side 1172 if (mBrowserFrame.committed() 1173 && !mBrowserFrame.firstLayoutDone()) { 1174 mBrowserFrame.didFirstLayout(); 1175 } 1176 // Do this after syncing up the layout state. 1177 stopLoading(); 1178 break; 1179 1180 case RELOAD: 1181 mBrowserFrame.reload(false); 1182 break; 1183 1184 case KEY_DOWN: 1185 key((KeyEvent) msg.obj, true); 1186 break; 1187 1188 case KEY_UP: 1189 key((KeyEvent) msg.obj, false); 1190 break; 1191 1192 case FAKE_CLICK: 1193 nativeClick(msg.arg1, msg.arg2, true); 1194 break; 1195 1196 case CLICK: 1197 nativeClick(msg.arg1, msg.arg2, false); 1198 break; 1199 1200 case VIEW_SIZE_CHANGED: { 1201 viewSizeChanged((WebView.ViewSizeData) msg.obj); 1202 break; 1203 } 1204 case SET_SCROLL_OFFSET: 1205 // note: these are in document coordinates 1206 // (inv-zoom) 1207 Point pt = (Point) msg.obj; 1208 nativeSetScrollOffset(msg.arg1, msg.arg2 == 1, 1209 pt.x, pt.y); 1210 break; 1211 1212 case SET_GLOBAL_BOUNDS: 1213 Rect r = (Rect) msg.obj; 1214 nativeSetGlobalBounds(r.left, r.top, r.width(), 1215 r.height()); 1216 break; 1217 1218 case GO_BACK_FORWARD: 1219 // If it is a standard load and the load is not 1220 // committed yet, we interpret BACK as RELOAD 1221 if (!mBrowserFrame.committed() && msg.arg1 == -1 && 1222 (mBrowserFrame.loadType() == 1223 BrowserFrame.FRAME_LOADTYPE_STANDARD)) { 1224 mBrowserFrame.reload(true); 1225 } else { 1226 mBrowserFrame.goBackOrForward(msg.arg1); 1227 } 1228 break; 1229 1230 case RESTORE_STATE: 1231 stopLoading(); 1232 restoreState(msg.arg1); 1233 break; 1234 1235 case PAUSE_TIMERS: 1236 mSavedPriority = Process.getThreadPriority(mTid); 1237 Process.setThreadPriority(mTid, 1238 Process.THREAD_PRIORITY_BACKGROUND); 1239 pauseTimers(); 1240 if (!JniUtil.useChromiumHttpStack()) { 1241 WebViewWorker.getHandler().sendEmptyMessage( 1242 WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION); 1243 } 1244 break; 1245 1246 case RESUME_TIMERS: 1247 Process.setThreadPriority(mTid, mSavedPriority); 1248 resumeTimers(); 1249 if (!JniUtil.useChromiumHttpStack()) { 1250 WebViewWorker.getHandler().sendEmptyMessage( 1251 WebViewWorker.MSG_RESUME_CACHE_TRANSACTION); 1252 } 1253 break; 1254 1255 case ON_PAUSE: 1256 nativePause(); 1257 break; 1258 1259 case ON_RESUME: 1260 nativeResume(); 1261 break; 1262 1263 case FREE_MEMORY: 1264 clearCache(false); 1265 nativeFreeMemory(); 1266 break; 1267 1268 case SET_NETWORK_STATE: 1269 if (BrowserFrame.sJavaBridge == null) { 1270 throw new IllegalStateException("No WebView " + 1271 "has been created in this process!"); 1272 } 1273 BrowserFrame.sJavaBridge 1274 .setNetworkOnLine(msg.arg1 == 1); 1275 break; 1276 1277 case SET_NETWORK_TYPE: 1278 if (BrowserFrame.sJavaBridge == null) { 1279 throw new IllegalStateException("No WebView " + 1280 "has been created in this process!"); 1281 } 1282 Map<String, String> map = (Map<String, String>) msg.obj; 1283 BrowserFrame.sJavaBridge 1284 .setNetworkType(map.get("type"), map.get("subtype")); 1285 break; 1286 1287 case CLEAR_CACHE: 1288 clearCache(msg.arg1 == 1); 1289 break; 1290 1291 case CLEAR_HISTORY: 1292 mCallbackProxy.getBackForwardList(). 1293 close(mBrowserFrame.mNativeFrame); 1294 break; 1295 1296 case REPLACE_TEXT: 1297 ReplaceTextData rep = (ReplaceTextData) msg.obj; 1298 nativeReplaceTextfieldText(msg.arg1, msg.arg2, 1299 rep.mReplace, rep.mNewStart, rep.mNewEnd, 1300 rep.mTextGeneration); 1301 break; 1302 1303 case PASS_TO_JS: { 1304 JSKeyData jsData = (JSKeyData) msg.obj; 1305 KeyEvent evt = jsData.mEvent; 1306 int keyCode = evt.getKeyCode(); 1307 int keyValue = evt.getUnicodeChar(); 1308 int generation = msg.arg1; 1309 passToJs(generation, 1310 jsData.mCurrentText, 1311 keyCode, 1312 keyValue, 1313 evt.isDown(), 1314 evt.isShiftPressed(), evt.isAltPressed(), 1315 evt.isSymPressed()); 1316 break; 1317 } 1318 1319 case SAVE_DOCUMENT_STATE: { 1320 CursorData cDat = (CursorData) msg.obj; 1321 nativeSaveDocumentState(cDat.mFrame); 1322 break; 1323 } 1324 1325 case CLEAR_SSL_PREF_TABLE: 1326 Network.getInstance(mContext) 1327 .clearUserSslPrefTable(); 1328 break; 1329 1330 case TOUCH_UP: 1331 TouchUpData touchUpData = (TouchUpData) msg.obj; 1332 if (touchUpData.mNativeLayer != 0) { 1333 nativeScrollLayer(touchUpData.mNativeLayer, 1334 touchUpData.mNativeLayerRect); 1335 } 1336 nativeTouchUp(touchUpData.mMoveGeneration, 1337 touchUpData.mFrame, touchUpData.mNode, 1338 touchUpData.mX, touchUpData.mY); 1339 break; 1340 1341 case TOUCH_EVENT: { 1342 TouchEventData ted = (TouchEventData) msg.obj; 1343 final int count = ted.mPoints.length; 1344 int[] xArray = new int[count]; 1345 int[] yArray = new int[count]; 1346 for (int c = 0; c < count; c++) { 1347 xArray[c] = ted.mPoints[c].x; 1348 yArray[c] = ted.mPoints[c].y; 1349 } 1350 if (ted.mNativeLayer != 0) { 1351 nativeScrollLayer(ted.mNativeLayer, 1352 ted.mNativeLayerRect); 1353 } 1354 ted.mNativeResult = nativeHandleTouchEvent(ted.mAction, ted.mIds, 1355 xArray, yArray, count, ted.mActionIndex, ted.mMetaState); 1356 Message.obtain( 1357 mWebView.mPrivateHandler, 1358 WebView.PREVENT_TOUCH_ID, 1359 ted.mAction, 1360 ted.mNativeResult ? 1 : 0, 1361 ted).sendToTarget(); 1362 break; 1363 } 1364 1365 case SET_ACTIVE: 1366 nativeSetFocusControllerActive(msg.arg1 == 1); 1367 break; 1368 1369 case ADD_JS_INTERFACE: 1370 JSInterfaceData jsData = (JSInterfaceData) msg.obj; 1371 mBrowserFrame.addJavascriptInterface(jsData.mObject, 1372 jsData.mInterfaceName); 1373 break; 1374 1375 case REMOVE_JS_INTERFACE: 1376 jsData = (JSInterfaceData) msg.obj; 1377 mBrowserFrame.removeJavascriptInterface( 1378 jsData.mInterfaceName); 1379 break; 1380 1381 case REQUEST_EXT_REPRESENTATION: 1382 mBrowserFrame.externalRepresentation( 1383 (Message) msg.obj); 1384 break; 1385 1386 case REQUEST_DOC_AS_TEXT: 1387 mBrowserFrame.documentAsText((Message) msg.obj); 1388 break; 1389 1390 case SET_MOVE_FOCUS: 1391 CursorData focusData = (CursorData) msg.obj; 1392 nativeMoveFocus(focusData.mFrame, focusData.mNode); 1393 break; 1394 1395 case SET_MOVE_MOUSE: 1396 CursorData cursorData = (CursorData) msg.obj; 1397 nativeMoveMouse(cursorData.mFrame, 1398 cursorData.mX, cursorData.mY); 1399 break; 1400 1401 case SET_MOVE_MOUSE_IF_LATEST: 1402 CursorData cData = (CursorData) msg.obj; 1403 nativeMoveMouseIfLatest(cData.mMoveGeneration, 1404 cData.mFrame, 1405 cData.mX, cData.mY); 1406 if (msg.arg1 == 1) { 1407 nativeStopPaintingCaret(); 1408 } 1409 break; 1410 1411 case REQUEST_CURSOR_HREF: { 1412 Message hrefMsg = (Message) msg.obj; 1413 hrefMsg.getData().putString("url", 1414 nativeRetrieveHref(msg.arg1, msg.arg2)); 1415 hrefMsg.getData().putString("title", 1416 nativeRetrieveAnchorText(msg.arg1, msg.arg2)); 1417 hrefMsg.getData().putString("src", 1418 nativeRetrieveImageSource(msg.arg1, msg.arg2)); 1419 hrefMsg.sendToTarget(); 1420 break; 1421 } 1422 1423 case UPDATE_CACHE_AND_TEXT_ENTRY: 1424 nativeUpdateFrameCache(); 1425 // FIXME: this should provide a minimal rectangle 1426 if (mWebView != null) { 1427 mWebView.postInvalidate(); 1428 } 1429 sendUpdateTextEntry(); 1430 break; 1431 1432 case DOC_HAS_IMAGES: 1433 Message imageResult = (Message) msg.obj; 1434 imageResult.arg1 = 1435 mBrowserFrame.documentHasImages() ? 1 : 0; 1436 imageResult.sendToTarget(); 1437 break; 1438 1439 case DELETE_SELECTION: 1440 TextSelectionData deleteSelectionData 1441 = (TextSelectionData) msg.obj; 1442 nativeDeleteSelection(deleteSelectionData.mStart, 1443 deleteSelectionData.mEnd, msg.arg1); 1444 break; 1445 1446 case SET_SELECTION: 1447 nativeSetSelection(msg.arg1, msg.arg2); 1448 break; 1449 1450 case MODIFY_SELECTION: 1451 String modifiedSelectionString = nativeModifySelection(msg.arg1, 1452 msg.arg2); 1453 mWebView.mPrivateHandler.obtainMessage(WebView.SELECTION_STRING_CHANGED, 1454 modifiedSelectionString).sendToTarget(); 1455 break; 1456 1457 case LISTBOX_CHOICES: 1458 SparseBooleanArray choices = (SparseBooleanArray) 1459 msg.obj; 1460 int choicesSize = msg.arg1; 1461 boolean[] choicesArray = new boolean[choicesSize]; 1462 for (int c = 0; c < choicesSize; c++) { 1463 choicesArray[c] = choices.get(c); 1464 } 1465 nativeSendListBoxChoices(choicesArray, 1466 choicesSize); 1467 break; 1468 1469 case SINGLE_LISTBOX_CHOICE: 1470 nativeSendListBoxChoice(msg.arg1); 1471 break; 1472 1473 case SET_BACKGROUND_COLOR: 1474 nativeSetBackgroundColor(msg.arg1); 1475 break; 1476 1477 case DUMP_DOMTREE: 1478 nativeDumpDomTree(msg.arg1 == 1); 1479 break; 1480 1481 case DUMP_RENDERTREE: 1482 nativeDumpRenderTree(msg.arg1 == 1); 1483 break; 1484 1485 case DUMP_NAVTREE: 1486 nativeDumpNavTree(); 1487 break; 1488 1489 case DUMP_V8COUNTERS: 1490 nativeDumpV8Counters(); 1491 break; 1492 1493 case SET_JS_FLAGS: 1494 nativeSetJsFlags((String)msg.obj); 1495 break; 1496 1497 case SAVE_WEBARCHIVE: 1498 WebView.SaveWebArchiveMessage saveMessage = 1499 (WebView.SaveWebArchiveMessage)msg.obj; 1500 saveMessage.mResultFile = 1501 saveWebArchive(saveMessage.mBasename, saveMessage.mAutoname); 1502 mWebView.mPrivateHandler.obtainMessage( 1503 WebView.SAVE_WEBARCHIVE_FINISHED, saveMessage).sendToTarget(); 1504 break; 1505 1506 case GEOLOCATION_PERMISSIONS_PROVIDE: 1507 GeolocationPermissionsData data = 1508 (GeolocationPermissionsData) msg.obj; 1509 nativeGeolocationPermissionsProvide(data.mOrigin, 1510 data.mAllow, data.mRemember); 1511 break; 1512 1513 case SPLIT_PICTURE_SET: 1514 nativeSplitContent(msg.arg1); 1515 mWebView.mPrivateHandler.obtainMessage( 1516 WebView.REPLACE_BASE_CONTENT, msg.arg1, 0); 1517 mSplitPictureIsScheduled = false; 1518 break; 1519 1520 case CLEAR_CONTENT: 1521 // Clear the view so that onDraw() will draw nothing 1522 // but white background 1523 // (See public method WebView.clearView) 1524 nativeClearContent(); 1525 break; 1526 1527 case MESSAGE_RELAY: 1528 ((Message) msg.obj).sendToTarget(); 1529 break; 1530 1531 case POPULATE_VISITED_LINKS: 1532 nativeProvideVisitedHistory((String[])msg.obj); 1533 break; 1534 1535 case VALID_NODE_BOUNDS: { 1536 MotionUpData motionUpData = (MotionUpData) msg.obj; 1537 if (!nativeValidNodeAndBounds( 1538 motionUpData.mFrame, motionUpData.mNode, 1539 motionUpData.mBounds)) { 1540 nativeUpdateFrameCache(); 1541 } 1542 Message message = mWebView.mPrivateHandler 1543 .obtainMessage(WebView.DO_MOTION_UP, 1544 motionUpData.mX, motionUpData.mY); 1545 mWebView.mPrivateHandler.sendMessageAtFrontOfQueue( 1546 message); 1547 break; 1548 } 1549 1550 case HIDE_FULLSCREEN: 1551 nativeFullScreenPluginHidden(msg.arg1); 1552 break; 1553 1554 case ADD_PACKAGE_NAMES: 1555 if (BrowserFrame.sJavaBridge == null) { 1556 throw new IllegalStateException("No WebView " + 1557 "has been created in this process!"); 1558 } 1559 BrowserFrame.sJavaBridge.addPackageNames( 1560 (Set<String>) msg.obj); 1561 break; 1562 1563 case GET_TOUCH_HIGHLIGHT_RECTS: 1564 TouchHighlightData d = (TouchHighlightData) msg.obj; 1565 ArrayList<Rect> rects = nativeGetTouchHighlightRects 1566 (d.mX, d.mY, d.mSlop); 1567 mWebView.mPrivateHandler.obtainMessage( 1568 WebView.SET_TOUCH_HIGHLIGHT_RECTS, rects) 1569 .sendToTarget(); 1570 break; 1571 1572 case REMOVE_TOUCH_HIGHLIGHT_RECTS: 1573 mWebView.mPrivateHandler.obtainMessage( 1574 WebView.SET_TOUCH_HIGHLIGHT_RECTS, null) 1575 .sendToTarget(); 1576 break; 1577 1578 case USE_MOCK_DEVICE_ORIENTATION: 1579 useMockDeviceOrientation(); 1580 break; 1581 1582 case AUTOFILL_FORM: 1583 nativeAutoFillForm(msg.arg1); 1584 mWebView.mPrivateHandler.obtainMessage(WebView.AUTOFILL_COMPLETE, null) 1585 .sendToTarget(); 1586 break; 1587 1588 case EXECUTE_JS: 1589 if (msg.obj instanceof String) { 1590 if (DebugFlags.WEB_VIEW_CORE) { 1591 Log.d(LOGTAG, "Executing JS : " + msg.obj); 1592 } 1593 mBrowserFrame.stringByEvaluatingJavaScriptFromString((String) msg.obj); 1594 } 1595 break; 1596 } 1597 } 1598 }; 1599 // Take all queued messages and resend them to the new handler. 1600 synchronized (this) { 1601 int size = mMessages.size(); 1602 for (int i = 0; i < size; i++) { 1603 mHandler.sendMessage(mMessages.get(i)); 1604 } 1605 mMessages = null; 1606 } 1607 } 1608 1609 /** 1610 * Send a message internally to the queue or to the handler 1611 */ 1612 private synchronized void sendMessage(Message msg) { 1613 if (mBlockMessages) { 1614 return; 1615 } 1616 if (mMessages != null) { 1617 mMessages.add(msg); 1618 } else { 1619 mHandler.sendMessage(msg); 1620 } 1621 } 1622 1623 private synchronized void removeMessages(int what) { 1624 if (mBlockMessages) { 1625 return; 1626 } 1627 if (what == EventHub.WEBKIT_DRAW) { 1628 mDrawIsScheduled = false; 1629 } 1630 if (mMessages != null) { 1631 Log.w(LOGTAG, "Not supported in this case."); 1632 } else { 1633 mHandler.removeMessages(what); 1634 } 1635 } 1636 1637 private synchronized boolean hasMessages(int what) { 1638 if (mBlockMessages) { 1639 return false; 1640 } 1641 if (mMessages != null) { 1642 Log.w(LOGTAG, "hasMessages() is not supported in this case."); 1643 return false; 1644 } else { 1645 return mHandler.hasMessages(what); 1646 } 1647 } 1648 1649 private synchronized void sendMessageDelayed(Message msg, long delay) { 1650 if (mBlockMessages) { 1651 return; 1652 } 1653 mHandler.sendMessageDelayed(msg, delay); 1654 } 1655 1656 /** 1657 * Send a message internally to the front of the queue. 1658 */ 1659 private synchronized void sendMessageAtFrontOfQueue(Message msg) { 1660 if (mBlockMessages) { 1661 return; 1662 } 1663 if (mMessages != null) { 1664 mMessages.add(0, msg); 1665 } else { 1666 mHandler.sendMessageAtFrontOfQueue(msg); 1667 } 1668 } 1669 1670 /** 1671 * Remove all the messages. 1672 */ 1673 private synchronized void removeMessages() { 1674 // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed 1675 mDrawIsScheduled = false; 1676 mSplitPictureIsScheduled = false; 1677 if (mMessages != null) { 1678 mMessages.clear(); 1679 } else { 1680 mHandler.removeCallbacksAndMessages(null); 1681 } 1682 } 1683 1684 /** 1685 * Block sending messages to the EventHub. 1686 */ 1687 private synchronized void blockMessages() { 1688 mBlockMessages = true; 1689 } 1690 } 1691 1692 //------------------------------------------------------------------------- 1693 // Methods called by host activity (in the same thread) 1694 //------------------------------------------------------------------------- 1695 1696 void stopLoading() { 1697 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading"); 1698 if (mBrowserFrame != null) { 1699 mBrowserFrame.stopLoading(); 1700 } 1701 } 1702 1703 //------------------------------------------------------------------------- 1704 // Methods called by WebView 1705 // If it refers to local variable, it needs synchronized(). 1706 // If it needs WebCore, it has to send message. 1707 //------------------------------------------------------------------------- 1708 1709 /** 1710 * @hide 1711 */ 1712 public void sendMessage(Message msg) { 1713 mEventHub.sendMessage(msg); 1714 } 1715 1716 void sendMessage(int what) { 1717 mEventHub.sendMessage(Message.obtain(null, what)); 1718 } 1719 1720 void sendMessage(int what, Object obj) { 1721 mEventHub.sendMessage(Message.obtain(null, what, obj)); 1722 } 1723 1724 void sendMessage(int what, int arg1) { 1725 // just ignore the second argument (make it 0) 1726 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0)); 1727 } 1728 1729 void sendMessage(int what, int arg1, int arg2) { 1730 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2)); 1731 } 1732 1733 void sendMessage(int what, int arg1, Object obj) { 1734 // just ignore the second argument (make it 0) 1735 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj)); 1736 } 1737 1738 void sendMessage(int what, int arg1, int arg2, Object obj) { 1739 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj)); 1740 } 1741 1742 void sendMessageAtFrontOfQueue(int what, Object obj) { 1743 mEventHub.sendMessageAtFrontOfQueue(Message.obtain( 1744 null, what, obj)); 1745 } 1746 1747 void sendMessageDelayed(int what, Object obj, long delay) { 1748 mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay); 1749 } 1750 1751 void removeMessages(int what) { 1752 mEventHub.removeMessages(what); 1753 } 1754 1755 void removeMessages() { 1756 mEventHub.removeMessages(); 1757 } 1758 1759 /** 1760 * Removes pending messages and trigger a DESTROY message to send to 1761 * WebCore. 1762 * Called from UI thread. 1763 */ 1764 void destroy() { 1765 // We don't want anyone to post a message between removing pending 1766 // messages and sending the destroy message. 1767 synchronized (mEventHub) { 1768 // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to 1769 // be preserved even the WebView is destroyed. 1770 // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS 1771 boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS); 1772 boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS); 1773 mEventHub.removeMessages(); 1774 mEventHub.sendMessageAtFrontOfQueue( 1775 Message.obtain(null, EventHub.DESTROY)); 1776 if (hasPause) { 1777 mEventHub.sendMessageAtFrontOfQueue( 1778 Message.obtain(null, EventHub.PAUSE_TIMERS)); 1779 } 1780 if (hasResume) { 1781 mEventHub.sendMessageAtFrontOfQueue( 1782 Message.obtain(null, EventHub.RESUME_TIMERS)); 1783 } 1784 mEventHub.blockMessages(); 1785 } 1786 } 1787 1788 //------------------------------------------------------------------------- 1789 // WebViewCore private methods 1790 //------------------------------------------------------------------------- 1791 1792 private void clearCache(boolean includeDiskFiles) { 1793 mBrowserFrame.clearCache(); 1794 if (includeDiskFiles) { 1795 CacheManager.removeAllCacheFiles(); 1796 } 1797 } 1798 1799 private void loadUrl(String url, Map<String, String> extraHeaders) { 1800 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url); 1801 mBrowserFrame.loadUrl(url, extraHeaders); 1802 } 1803 1804 private String saveWebArchive(String filename, boolean autoname) { 1805 if (DebugFlags.WEB_VIEW_CORE) { 1806 Log.v(LOGTAG, " CORE saveWebArchive " + filename + " " + autoname); 1807 } 1808 return mBrowserFrame.saveWebArchive(filename, autoname); 1809 } 1810 1811 private void key(KeyEvent evt, boolean isDown) { 1812 if (DebugFlags.WEB_VIEW_CORE) { 1813 Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", " 1814 + evt); 1815 } 1816 int keyCode = evt.getKeyCode(); 1817 int unicodeChar = evt.getUnicodeChar(); 1818 1819 if (keyCode == KeyEvent.KEYCODE_UNKNOWN && evt.getCharacters() != null 1820 && evt.getCharacters().length() > 0) { 1821 // we should only receive individual complex characters 1822 unicodeChar = evt.getCharacters().codePointAt(0); 1823 } 1824 1825 if (!nativeKey(keyCode, unicodeChar, evt.getRepeatCount(), evt.isShiftPressed(), 1826 evt.isAltPressed(), evt.isSymPressed(), 1827 isDown) && keyCode != KeyEvent.KEYCODE_ENTER) { 1828 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP 1829 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { 1830 if (DebugFlags.WEB_VIEW_CORE) { 1831 Log.v(LOGTAG, "key: arrow unused by page: " + keyCode); 1832 } 1833 if (mWebView != null && evt.isDown()) { 1834 Message.obtain(mWebView.mPrivateHandler, 1835 WebView.UNHANDLED_NAV_KEY, keyCode, 1836 0).sendToTarget(); 1837 } 1838 return; 1839 } 1840 // bubble up the event handling 1841 // but do not bubble up the ENTER key, which would open the search 1842 // bar without any text. 1843 mCallbackProxy.onUnhandledKeyEvent(evt); 1844 } 1845 } 1846 1847 // These values are used to avoid requesting a layout based on old values 1848 private int mCurrentViewWidth = 0; 1849 private int mCurrentViewHeight = 0; 1850 private float mCurrentViewScale = 1.0f; 1851 1852 // notify webkit that our virtual view size changed size (after inv-zoom) 1853 private void viewSizeChanged(WebView.ViewSizeData data) { 1854 int w = data.mWidth; 1855 int h = data.mHeight; 1856 int textwrapWidth = data.mTextWrapWidth; 1857 float scale = data.mScale; 1858 if (DebugFlags.WEB_VIEW_CORE) { 1859 Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h 1860 + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale); 1861 } 1862 if (w == 0) { 1863 Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); 1864 return; 1865 } 1866 int width = calculateWindowWidth(w); 1867 int height = h; 1868 if (width != w) { 1869 float heightWidthRatio = data.mHeightWidthRatio; 1870 float ratio = (heightWidthRatio > 0) ? heightWidthRatio : (float) h / w; 1871 height = Math.round(ratio * width); 1872 } 1873 nativeSetSize(width, height, textwrapWidth, scale, w, 1874 data.mActualViewHeight > 0 ? data.mActualViewHeight : h, 1875 data.mAnchorX, data.mAnchorY, data.mIgnoreHeight); 1876 // Remember the current width and height 1877 boolean needInvalidate = (mCurrentViewWidth == 0); 1878 mCurrentViewWidth = w; 1879 mCurrentViewHeight = h; 1880 mCurrentViewScale = scale; 1881 if (needInvalidate) { 1882 // ensure {@link #webkitDraw} is called as we were blocking in 1883 // {@link #contentDraw} when mCurrentViewWidth is 0 1884 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged"); 1885 contentDraw(); 1886 } 1887 mEventHub.sendMessage(Message.obtain(null, 1888 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY)); 1889 } 1890 1891 // Calculate width to be used in webkit window. 1892 private int calculateWindowWidth(int viewWidth) { 1893 int width = viewWidth; 1894 if (mSettings.getUseWideViewPort()) { 1895 if (mViewportWidth == -1) { 1896 // Fixed viewport width. 1897 width = WebView.DEFAULT_VIEWPORT_WIDTH; 1898 } else if (mViewportWidth > 0) { 1899 // Use website specified or desired fixed viewport width. 1900 width = mViewportWidth; 1901 } else { 1902 // For mobile web site. 1903 width = Math.round(mWebView.getViewWidth() / mWebView.getDefaultZoomScale()); 1904 } 1905 } 1906 return width; 1907 } 1908 1909 private void sendUpdateTextEntry() { 1910 if (mWebView != null) { 1911 Message.obtain(mWebView.mPrivateHandler, 1912 WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget(); 1913 } 1914 } 1915 1916 // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize 1917 // callbacks. Computes the sum of database quota for all origins. 1918 private long getUsedQuota() { 1919 WebStorage webStorage = WebStorage.getInstance(); 1920 Collection<WebStorage.Origin> origins = webStorage.getOriginsSync(); 1921 1922 if (origins == null) { 1923 return 0; 1924 } 1925 long usedQuota = 0; 1926 for (WebStorage.Origin website : origins) { 1927 usedQuota += website.getQuota(); 1928 } 1929 return usedQuota; 1930 } 1931 1932 // called from UI thread 1933 void splitContent(int content) { 1934 if (!mSplitPictureIsScheduled) { 1935 mSplitPictureIsScheduled = true; 1936 sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0); 1937 } 1938 } 1939 1940 // Used to avoid posting more than one draw message. 1941 private boolean mDrawIsScheduled; 1942 private boolean mDrawLayersIsScheduled; 1943 1944 // Used to avoid posting more than one split picture message. 1945 private boolean mSplitPictureIsScheduled; 1946 1947 // Used to suspend drawing. 1948 private boolean mDrawIsPaused; 1949 1950 // mInitialViewState is set by didFirstLayout() and then reset in the 1951 // next webkitDraw after passing the state to the UI thread. 1952 private ViewState mInitialViewState = null; 1953 1954 static class ViewState { 1955 float mMinScale; 1956 float mMaxScale; 1957 float mViewScale; 1958 float mTextWrapScale; 1959 float mDefaultScale; 1960 int mScrollX; 1961 int mScrollY; 1962 boolean mMobileSite; 1963 boolean mIsRestored; 1964 } 1965 1966 static class DrawData { 1967 DrawData() { 1968 mBaseLayer = 0; 1969 mInvalRegion = new Region(); 1970 mContentSize = new Point(); 1971 } 1972 int mBaseLayer; 1973 Region mInvalRegion; 1974 // view size that was used by webkit during the most recent layout 1975 Point mViewSize; 1976 Point mContentSize; 1977 int mMinPrefWidth; 1978 // only non-null if it is for the first picture set after the first layout 1979 ViewState mViewState; 1980 boolean mFocusSizeChanged; 1981 } 1982 1983 // Only update the layers' content, not the base surface 1984 // PictureSet. 1985 private void webkitDrawLayers() { 1986 mDrawLayersIsScheduled = false; 1987 if (mDrawIsScheduled) { 1988 removeMessages(EventHub.WEBKIT_DRAW); 1989 webkitDraw(); 1990 return; 1991 } 1992 DrawData draw = new DrawData(); 1993 draw.mBaseLayer = nativeUpdateLayers(draw.mInvalRegion); 1994 webkitDraw(draw); 1995 } 1996 1997 private void webkitDraw() { 1998 mDrawIsScheduled = false; 1999 DrawData draw = new DrawData(); 2000 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); 2001 draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mContentSize); 2002 if (draw.mBaseLayer == 0) { 2003 if (mWebView != null && !mWebView.isPaused()) { 2004 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message"); 2005 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 2006 } else { 2007 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, webview paused"); 2008 } 2009 return; 2010 } 2011 webkitDraw(draw); 2012 } 2013 2014 private void webkitDraw(DrawData draw) { 2015 if (mWebView != null) { 2016 draw.mFocusSizeChanged = nativeFocusBoundsChanged(); 2017 draw.mViewSize = new Point(mCurrentViewWidth, mCurrentViewHeight); 2018 if (mSettings.getUseWideViewPort()) { 2019 draw.mMinPrefWidth = Math.max( 2020 mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH 2021 : (mViewportWidth == 0 ? mCurrentViewWidth 2022 : mViewportWidth), 2023 nativeGetContentMinPrefWidth()); 2024 } 2025 if (mInitialViewState != null) { 2026 draw.mViewState = mInitialViewState; 2027 mInitialViewState = null; 2028 } 2029 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); 2030 Message.obtain(mWebView.mPrivateHandler, 2031 WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); 2032 } 2033 } 2034 2035 static void reducePriority() { 2036 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 2037 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 2038 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 2039 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 2040 .obtainMessage(WebCoreThread.REDUCE_PRIORITY)); 2041 } 2042 2043 static void resumePriority() { 2044 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 2045 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 2046 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 2047 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 2048 .obtainMessage(WebCoreThread.RESUME_PRIORITY)); 2049 } 2050 2051 static void sendStaticMessage(int messageType, Object argument) { 2052 if (sWebCoreHandler == null) 2053 return; 2054 2055 sWebCoreHandler.sendMessage(sWebCoreHandler.obtainMessage(messageType, argument)); 2056 } 2057 2058 static void pauseUpdatePicture(WebViewCore core) { 2059 // Note: there is one possible failure mode. If pauseUpdatePicture() is 2060 // called from UI thread while WEBKIT_DRAW is just pulled out of the 2061 // queue in WebCore thread to be executed. Then update won't be blocked. 2062 if (core != null) { 2063 if (!core.getSettings().enableSmoothTransition()) return; 2064 2065 synchronized (core) { 2066 core.mDrawIsPaused = true; 2067 if (core.mDrawIsScheduled) { 2068 core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW); 2069 } 2070 } 2071 } 2072 2073 } 2074 2075 static void resumeUpdatePicture(WebViewCore core) { 2076 if (core != null) { 2077 // if mDrawIsPaused is true, ignore the setting, continue to resume 2078 if (!core.mDrawIsPaused 2079 && !core.getSettings().enableSmoothTransition()) return; 2080 2081 synchronized (core) { 2082 core.mDrawIsPaused = false; 2083 // always redraw on resume to reenable gif animations 2084 core.mDrawIsScheduled = false; 2085 core.nativeContentInvalidateAll(); 2086 core.contentDraw(); 2087 } 2088 } 2089 } 2090 2091 ////////////////////////////////////////////////////////////////////////// 2092 2093 private void restoreState(int index) { 2094 WebBackForwardList list = mCallbackProxy.getBackForwardList(); 2095 int size = list.getSize(); 2096 for (int i = 0; i < size; i++) { 2097 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); 2098 } 2099 mBrowserFrame.mLoadInitFromJava = true; 2100 list.restoreIndex(mBrowserFrame.mNativeFrame, index); 2101 mBrowserFrame.mLoadInitFromJava = false; 2102 } 2103 2104 //------------------------------------------------------------------------- 2105 // Implement abstract methods in WebViewCore, native WebKit callback part 2106 //------------------------------------------------------------------------- 2107 2108 // called from JNI or WebView thread 2109 /* package */ void contentDraw() { 2110 // don't update the Picture until we have an initial width and finish 2111 // the first layout 2112 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { 2113 return; 2114 } 2115 // only fire an event if this is our first request 2116 synchronized (this) { 2117 if (mDrawIsScheduled) return; 2118 mDrawIsScheduled = true; 2119 if (mDrawIsPaused) return; 2120 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 2121 } 2122 } 2123 2124 // called from JNI 2125 void layersDraw() { 2126 synchronized (this) { 2127 if (mDrawLayersIsScheduled) return; 2128 mDrawLayersIsScheduled = true; 2129 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW_LAYERS)); 2130 } 2131 } 2132 2133 // called by JNI 2134 private void contentScrollTo(int x, int y, boolean animate, 2135 boolean onlyIfImeIsShowing) { 2136 if (!mBrowserFrame.firstLayoutDone()) { 2137 /* 2138 * WebKit restore state will be called before didFirstLayout(), 2139 * remember the position as it has to be applied after restoring 2140 * zoom factor which is controlled by screenWidth. 2141 */ 2142 mRestoredX = x; 2143 mRestoredY = y; 2144 return; 2145 } 2146 if (mWebView != null) { 2147 Message msg = Message.obtain(mWebView.mPrivateHandler, 2148 WebView.SCROLL_TO_MSG_ID, animate ? 1 : 0, 2149 onlyIfImeIsShowing ? 1 : 0, new Point(x, y)); 2150 if (mDrawIsScheduled) { 2151 mEventHub.sendMessage(Message.obtain(null, 2152 EventHub.MESSAGE_RELAY, msg)); 2153 } else { 2154 msg.sendToTarget(); 2155 } 2156 } 2157 } 2158 2159 // called by JNI 2160 private void sendNotifyProgressFinished() { 2161 sendUpdateTextEntry(); 2162 if (!JniUtil.useChromiumHttpStack()) { 2163 // as CacheManager can behave based on database transaction, we need to 2164 // call tick() to trigger endTransaction 2165 WebViewWorker.getHandler().removeMessages( 2166 WebViewWorker.MSG_CACHE_TRANSACTION_TICKER); 2167 WebViewWorker.getHandler().sendEmptyMessage( 2168 WebViewWorker.MSG_CACHE_TRANSACTION_TICKER); 2169 } 2170 contentDraw(); 2171 } 2172 2173 /* Called by JNI. The coordinates are in doc coordinates, so they need to 2174 be scaled before they can be used by the view system, which happens 2175 in WebView since it (and its thread) know the current scale factor. 2176 */ 2177 private void sendViewInvalidate(int left, int top, int right, int bottom) { 2178 if (mWebView != null) { 2179 Message.obtain(mWebView.mPrivateHandler, 2180 WebView.INVAL_RECT_MSG_ID, 2181 new Rect(left, top, right, bottom)).sendToTarget(); 2182 } 2183 } 2184 2185 private static boolean mRepaintScheduled = false; 2186 2187 /* 2188 * Called by the WebView thread 2189 */ 2190 /* package */ void signalRepaintDone() { 2191 mRepaintScheduled = false; 2192 } 2193 2194 /* package */ WebView getWebView() { 2195 return mWebView; 2196 } 2197 2198 private native void setViewportSettingsFromNative(); 2199 2200 // called by JNI 2201 private void didFirstLayout(boolean standardLoad) { 2202 if (DebugFlags.WEB_VIEW_CORE) { 2203 Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad); 2204 } 2205 2206 mBrowserFrame.didFirstLayout(); 2207 2208 if (mWebView == null) return; 2209 2210 boolean updateViewState = standardLoad || mRestoredScale > 0; 2211 setupViewport(updateViewState); 2212 // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will 2213 // be called after the WebView updates its state. If updateRestoreState 2214 // is false, start to draw now as it is ready. 2215 if (!updateViewState) { 2216 mWebView.mViewManager.postReadyToDrawAll(); 2217 } 2218 2219 // remove the touch highlight when moving to a new page 2220 if (getSettings().supportTouchOnly()) { 2221 mEventHub.sendMessage(Message.obtain(null, 2222 EventHub.REMOVE_TOUCH_HIGHLIGHT_RECTS)); 2223 } 2224 2225 // reset the scroll position, the restored offset and scales 2226 mRestoredX = mRestoredY = 0; 2227 mRestoredScale = mRestoredTextWrapScale = 0; 2228 } 2229 2230 // called by JNI 2231 private void updateViewport() { 2232 // if updateViewport is called before first layout, wait until first 2233 // layout to update the viewport. In the rare case, this is called after 2234 // first layout, force an update as we have just parsed the viewport 2235 // meta tag. 2236 if (mBrowserFrame.firstLayoutDone()) { 2237 setupViewport(true); 2238 } 2239 } 2240 2241 private void setupViewport(boolean updateViewState) { 2242 // set the viewport settings from WebKit 2243 setViewportSettingsFromNative(); 2244 2245 if (mSettings.forceUserScalable()) { 2246 mViewportUserScalable = true; 2247 if (mViewportInitialScale > 0) { 2248 if (mViewportMinimumScale > 0) { 2249 mViewportMinimumScale = Math.min(mViewportMinimumScale, 2250 mViewportInitialScale / 2); 2251 } 2252 if (mViewportMaximumScale > 0) { 2253 mViewportMaximumScale = Math.max(mViewportMaximumScale, 2254 mViewportInitialScale * 2); 2255 } 2256 } else { 2257 if (mViewportMinimumScale > 0) { 2258 mViewportMinimumScale = Math.min(mViewportMinimumScale, 50); 2259 } 2260 if (mViewportMaximumScale > 0) { 2261 mViewportMaximumScale = Math.max(mViewportMaximumScale, 200); 2262 } 2263 } 2264 } 2265 2266 // adjust the default scale to match the densityDpi 2267 float adjust = 1.0f; 2268 if (mViewportDensityDpi == -1) { 2269 // convert default zoom scale to a integer (percentage) to avoid any 2270 // issues with floating point comparisons 2271 if (mWebView != null && (int)(mWebView.getDefaultZoomScale() * 100) != 100) { 2272 adjust = mWebView.getDefaultZoomScale(); 2273 } 2274 } else if (mViewportDensityDpi > 0) { 2275 adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi 2276 / mViewportDensityDpi; 2277 } 2278 int defaultScale = (int) (adjust * 100); 2279 2280 if (mViewportInitialScale > 0) { 2281 mViewportInitialScale *= adjust; 2282 } 2283 if (mViewportMinimumScale > 0) { 2284 mViewportMinimumScale *= adjust; 2285 } 2286 if (mViewportMaximumScale > 0) { 2287 mViewportMaximumScale *= adjust; 2288 } 2289 2290 // infer the values if they are not defined. 2291 if (mViewportWidth == 0) { 2292 if (mViewportInitialScale == 0) { 2293 mViewportInitialScale = defaultScale; 2294 } 2295 } 2296 if (mViewportUserScalable == false) { 2297 mViewportInitialScale = defaultScale; 2298 mViewportMinimumScale = defaultScale; 2299 mViewportMaximumScale = defaultScale; 2300 } 2301 if (mViewportMinimumScale > mViewportInitialScale 2302 && mViewportInitialScale != 0) { 2303 mViewportMinimumScale = mViewportInitialScale; 2304 } 2305 if (mViewportMaximumScale > 0 2306 && mViewportMaximumScale < mViewportInitialScale) { 2307 mViewportMaximumScale = mViewportInitialScale; 2308 } 2309 if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) { 2310 mViewportWidth = 0; 2311 } 2312 2313 // if mViewportWidth is 0, it means device-width, always update. 2314 if (mViewportWidth != 0 && !updateViewState) { 2315 ViewState viewState = new ViewState(); 2316 viewState.mMinScale = mViewportMinimumScale / 100.0f; 2317 viewState.mMaxScale = mViewportMaximumScale / 100.0f; 2318 viewState.mDefaultScale = adjust; 2319 // as mViewportWidth is not 0, it is not mobile site. 2320 viewState.mMobileSite = false; 2321 // for non-mobile site, we don't need minPrefWidth, set it as 0 2322 viewState.mScrollX = 0; 2323 Message.obtain(mWebView.mPrivateHandler, 2324 WebView.UPDATE_ZOOM_RANGE, viewState).sendToTarget(); 2325 return; 2326 } 2327 2328 // now notify webview 2329 // webViewWidth refers to the width in the view system 2330 int webViewWidth; 2331 // viewportWidth refers to the width in the document system 2332 int viewportWidth = mCurrentViewWidth; 2333 if (viewportWidth == 0) { 2334 // this may happen when WebView just starts. This is not perfect as 2335 // we call WebView method from WebCore thread. But not perfect 2336 // reference is better than no reference. 2337 webViewWidth = mWebView.getViewWidth(); 2338 viewportWidth = (int) (webViewWidth / adjust); 2339 if (viewportWidth == 0) { 2340 Log.w(LOGTAG, "Can't get the viewWidth after the first layout"); 2341 } 2342 } else { 2343 webViewWidth = Math.round(viewportWidth * mCurrentViewScale); 2344 } 2345 mInitialViewState = new ViewState(); 2346 mInitialViewState.mMinScale = mViewportMinimumScale / 100.0f; 2347 mInitialViewState.mMaxScale = mViewportMaximumScale / 100.0f; 2348 mInitialViewState.mDefaultScale = adjust; 2349 mInitialViewState.mScrollX = mRestoredX; 2350 mInitialViewState.mScrollY = mRestoredY; 2351 mInitialViewState.mMobileSite = (0 == mViewportWidth); 2352 if (mRestoredScale > 0) { 2353 mInitialViewState.mIsRestored = true; 2354 mInitialViewState.mViewScale = mRestoredScale; 2355 if (mRestoredTextWrapScale > 0) { 2356 mInitialViewState.mTextWrapScale = mRestoredTextWrapScale; 2357 } else { 2358 mInitialViewState.mTextWrapScale = mInitialViewState.mViewScale; 2359 } 2360 } else { 2361 if (mViewportInitialScale > 0) { 2362 mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale = 2363 mViewportInitialScale / 100.0f; 2364 } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth && 2365 !getSettings().getUseFixedViewport()) { 2366 mInitialViewState.mViewScale = mInitialViewState.mTextWrapScale = 2367 (float) webViewWidth / mViewportWidth; 2368 } else { 2369 mInitialViewState.mTextWrapScale = adjust; 2370 // 0 will trigger WebView to turn on zoom overview mode 2371 mInitialViewState.mViewScale = 0; 2372 } 2373 } 2374 2375 if (mWebView.mHeightCanMeasure) { 2376 // Trick to ensure that the Picture has the exact height for the 2377 // content by forcing to layout with 0 height after the page is 2378 // ready, which is indicated by didFirstLayout. This is essential to 2379 // get rid of the white space in the GMail which uses WebView for 2380 // message view. 2381 mWebView.mLastHeightSent = 0; 2382 // Send a negative scale to indicate that WebCore should reuse 2383 // the current scale 2384 WebView.ViewSizeData data = new WebView.ViewSizeData(); 2385 data.mWidth = mWebView.mLastWidthSent; 2386 data.mHeight = 0; 2387 // if mHeightCanMeasure is true, getUseWideViewPort() can't be 2388 // true. It is safe to use mWidth for mTextWrapWidth. 2389 data.mTextWrapWidth = data.mWidth; 2390 data.mScale = -1.0f; 2391 data.mIgnoreHeight = false; 2392 data.mAnchorX = data.mAnchorY = 0; 2393 // send VIEW_SIZE_CHANGED to the front of the queue so that we can 2394 // avoid pushing the wrong picture to the WebView side. If there is 2395 // a VIEW_SIZE_CHANGED in the queue, probably from WebView side, 2396 // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED 2397 // in the queue, as mLastHeightSent has been updated here, we may 2398 // miss the requestLayout in WebView side after the new picture. 2399 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2400 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 2401 EventHub.VIEW_SIZE_CHANGED, data)); 2402 } else if (mSettings.getUseWideViewPort()) { 2403 if (viewportWidth == 0) { 2404 // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView 2405 // to WebViewCore 2406 mWebView.mLastWidthSent = 0; 2407 } else { 2408 WebView.ViewSizeData data = new WebView.ViewSizeData(); 2409 // mViewScale as 0 means it is in zoom overview mode. So we don't 2410 // know the exact scale. If mRestoredScale is non-zero, use it; 2411 // otherwise just use mTextWrapScale as the initial scale. 2412 float tentativeScale = mInitialViewState.mViewScale; 2413 if (tentativeScale == 0) { 2414 // The following tries to figure out more correct view scale 2415 // and text wrap scale to be sent to webkit, by using some 2416 // knowledge from web settings and zoom manager. 2417 2418 // Calculated window width will be used to guess the scale 2419 // in zoom overview mode. 2420 tentativeScale = mInitialViewState.mTextWrapScale; 2421 int tentativeViewWidth = Math.round(webViewWidth / tentativeScale); 2422 int windowWidth = calculateWindowWidth(tentativeViewWidth); 2423 // In viewport setup time, since no content width is known, we assume 2424 // the windowWidth will be the content width, to get a more likely 2425 // zoom overview scale. 2426 data.mScale = (float) webViewWidth / windowWidth; 2427 if (!mSettings.getLoadWithOverviewMode()) { 2428 // If user choose non-overview mode. 2429 data.mScale = Math.max(data.mScale, tentativeScale); 2430 } 2431 if (mSettings.isNarrowColumnLayout()) { 2432 // In case of automatic text reflow in fixed view port mode. 2433 mInitialViewState.mTextWrapScale = 2434 ZoomManager.computeReadingLevelScale(data.mScale); 2435 } 2436 } else { 2437 // Scale is given such as when page is restored, use it. 2438 data.mScale = tentativeScale; 2439 } 2440 if (DebugFlags.WEB_VIEW_CORE) { 2441 Log.v(LOGTAG, "setupViewport" 2442 + " mRestoredScale=" + mRestoredScale 2443 + " mViewScale=" + mInitialViewState.mViewScale 2444 + " mTextWrapScale=" + mInitialViewState.mTextWrapScale 2445 + " data.mScale= " + data.mScale 2446 ); 2447 } 2448 data.mWidth = Math.round(webViewWidth / data.mScale); 2449 // We may get a call here when mCurrentViewHeight == 0 if webcore completes the 2450 // first layout before we sync our webview dimensions to it. In that case, we 2451 // request the real height of the webview. This is not a perfect solution as we 2452 // are calling a WebView method from the WebCore thread. But this is preferable 2453 // to syncing an incorrect height. 2454 data.mHeight = mCurrentViewHeight == 0 ? 2455 Math.round(mWebView.getViewHeight() / data.mScale) 2456 : Math.round((float) mCurrentViewHeight * data.mWidth / viewportWidth); 2457 data.mTextWrapWidth = Math.round(webViewWidth 2458 / mInitialViewState.mTextWrapScale); 2459 data.mIgnoreHeight = false; 2460 data.mAnchorX = data.mAnchorY = 0; 2461 // send VIEW_SIZE_CHANGED to the front of the queue so that we 2462 // can avoid pushing the wrong picture to the WebView side. 2463 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2464 // Let webkit know the scale and inner width/height immediately 2465 // in viewport setup time to avoid wrong information. 2466 viewSizeChanged(data); 2467 } 2468 } 2469 } 2470 2471 // called by JNI 2472 private void restoreScale(float scale, float textWrapScale) { 2473 if (mBrowserFrame.firstLayoutDone() == false) { 2474 final float defaultScale = mWebView.getDefaultZoomScale(); 2475 mRestoredScale = (scale <= 0.0) ? defaultScale : scale; 2476 if (mSettings.getUseWideViewPort()) { 2477 mRestoredTextWrapScale = (textWrapScale <= 0.0) ? defaultScale : textWrapScale; 2478 } 2479 } 2480 } 2481 2482 // called by JNI 2483 private void needTouchEvents(boolean need) { 2484 if (mWebView != null) { 2485 Message.obtain(mWebView.mPrivateHandler, 2486 WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0) 2487 .sendToTarget(); 2488 } 2489 } 2490 2491 // called by JNI 2492 private void updateTextfield(int ptr, boolean changeToPassword, 2493 String text, int textGeneration) { 2494 if (mWebView != null) { 2495 Message msg = Message.obtain(mWebView.mPrivateHandler, 2496 WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 2497 textGeneration, text); 2498 msg.getData().putBoolean("password", changeToPassword); 2499 msg.sendToTarget(); 2500 } 2501 } 2502 2503 // called by JNI 2504 private void updateTextSelection(int pointer, int start, int end, 2505 int textGeneration) { 2506 if (mWebView != null) { 2507 Message.obtain(mWebView.mPrivateHandler, 2508 WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration, 2509 new TextSelectionData(start, end)).sendToTarget(); 2510 } 2511 } 2512 2513 // called by JNI 2514 private void clearTextEntry() { 2515 if (mWebView == null) return; 2516 Message.obtain(mWebView.mPrivateHandler, 2517 WebView.CLEAR_TEXT_ENTRY).sendToTarget(); 2518 } 2519 2520 // called by JNI 2521 private void sendFindAgain() { 2522 if (mWebView == null) return; 2523 Message.obtain(mWebView.mPrivateHandler, 2524 WebView.FIND_AGAIN).sendToTarget(); 2525 } 2526 2527 private native void nativeUpdateFrameCacheIfLoading(); 2528 private native void nativeRevealSelection(); 2529 private native String nativeRequestLabel(int framePtr, int nodePtr); 2530 /** 2531 * Scroll the focused textfield to (xPercent, y) in document space 2532 */ 2533 private native void nativeScrollFocusedTextInput(float xPercent, int y); 2534 2535 // these must be in document space (i.e. not scaled/zoomed). 2536 private native void nativeSetScrollOffset(int gen, boolean sendScrollEvent, int dx, int dy); 2537 2538 private native void nativeSetGlobalBounds(int x, int y, int w, int h); 2539 2540 // called by JNI 2541 private void requestListBox(String[] array, int[] enabledArray, 2542 int[] selectedArray) { 2543 if (mWebView != null) { 2544 mWebView.requestListBox(array, enabledArray, selectedArray); 2545 } 2546 } 2547 2548 // called by JNI 2549 private void requestListBox(String[] array, int[] enabledArray, 2550 int selection) { 2551 if (mWebView != null) { 2552 mWebView.requestListBox(array, enabledArray, selection); 2553 } 2554 2555 } 2556 2557 // called by JNI 2558 private void requestKeyboardWithSelection(int pointer, int selStart, 2559 int selEnd, int textGeneration) { 2560 if (mWebView != null) { 2561 Message.obtain(mWebView.mPrivateHandler, 2562 WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer, 2563 textGeneration, new TextSelectionData(selStart, selEnd)) 2564 .sendToTarget(); 2565 } 2566 } 2567 2568 // called by JNI 2569 private void requestKeyboard(boolean showKeyboard) { 2570 if (mWebView != null) { 2571 Message.obtain(mWebView.mPrivateHandler, 2572 WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0) 2573 .sendToTarget(); 2574 } 2575 } 2576 2577 private void setWebTextViewAutoFillable(int queryId, String preview) { 2578 if (mWebView != null) { 2579 Message.obtain(mWebView.mPrivateHandler, WebView.SET_AUTOFILLABLE, 2580 new AutoFillData(queryId, preview)) 2581 .sendToTarget(); 2582 } 2583 } 2584 2585 // called by JNI 2586 private Context getContext() { 2587 return mContext; 2588 } 2589 2590 // called by JNI 2591 private void keepScreenOn(boolean screenOn) { 2592 if (mWebView != null) { 2593 Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SCREEN_ON); 2594 message.arg1 = screenOn ? 1 : 0; 2595 message.sendToTarget(); 2596 } 2597 } 2598 2599 // called by JNI 2600 private Class<?> getPluginClass(String libName, String clsName) { 2601 2602 if (mWebView == null) { 2603 return null; 2604 } 2605 2606 PluginManager pluginManager = PluginManager.getInstance(null); 2607 2608 String pkgName = pluginManager.getPluginsAPKName(libName); 2609 if (pkgName == null) { 2610 Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); 2611 return null; 2612 } 2613 2614 try { 2615 return pluginManager.getPluginClass(pkgName, clsName); 2616 } catch (NameNotFoundException e) { 2617 Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")"); 2618 } catch (ClassNotFoundException e) { 2619 Log.e(LOGTAG, "Unable to find plugin class (" + clsName + 2620 ") in the apk (" + pkgName + ")"); 2621 } 2622 2623 return null; 2624 } 2625 2626 // called by JNI. PluginWidget function to launch a full-screen view using a 2627 // View object provided by the plugin class. 2628 private void showFullScreenPlugin(ViewManager.ChildView childView, int orientation, int npp) { 2629 if (mWebView == null) { 2630 return; 2631 } 2632 2633 Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN); 2634 message.obj = childView.mView; 2635 message.arg1 = orientation; 2636 message.arg2 = npp; 2637 message.sendToTarget(); 2638 } 2639 2640 // called by JNI 2641 private void hideFullScreenPlugin() { 2642 if (mWebView == null) { 2643 return; 2644 } 2645 mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN) 2646 .sendToTarget(); 2647 } 2648 2649 private ViewManager.ChildView createSurface(View pluginView) { 2650 if (mWebView == null) { 2651 return null; 2652 } 2653 2654 if (pluginView == null) { 2655 Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy"); 2656 return null; 2657 } 2658 2659 // ensures the view system knows the view can redraw itself 2660 pluginView.setWillNotDraw(false); 2661 2662 if(pluginView instanceof SurfaceView) 2663 ((SurfaceView)pluginView).setZOrderOnTop(true); 2664 2665 ViewManager.ChildView view = mWebView.mViewManager.createView(); 2666 view.mView = pluginView; 2667 return view; 2668 } 2669 2670 // called by JNI. PluginWidget functions for creating an embedded View for 2671 // the surface drawing model. 2672 private ViewManager.ChildView addSurface(View pluginView, int x, int y, 2673 int width, int height) { 2674 ViewManager.ChildView view = createSurface(pluginView); 2675 view.attachView(x, y, width, height); 2676 return view; 2677 } 2678 2679 private void updateSurface(ViewManager.ChildView childView, int x, int y, 2680 int width, int height) { 2681 childView.attachView(x, y, width, height); 2682 } 2683 2684 private void destroySurface(ViewManager.ChildView childView) { 2685 childView.removeView(); 2686 } 2687 2688 // called by JNI 2689 static class ShowRectData { 2690 int mLeft; 2691 int mTop; 2692 int mWidth; 2693 int mHeight; 2694 int mContentWidth; 2695 int mContentHeight; 2696 float mXPercentInDoc; 2697 float mXPercentInView; 2698 float mYPercentInDoc; 2699 float mYPercentInView; 2700 } 2701 2702 private void showRect(int left, int top, int width, int height, 2703 int contentWidth, int contentHeight, float xPercentInDoc, 2704 float xPercentInView, float yPercentInDoc, float yPercentInView) { 2705 if (mWebView != null) { 2706 ShowRectData data = new ShowRectData(); 2707 data.mLeft = left; 2708 data.mTop = top; 2709 data.mWidth = width; 2710 data.mHeight = height; 2711 data.mContentWidth = contentWidth; 2712 data.mContentHeight = contentHeight; 2713 data.mXPercentInDoc = xPercentInDoc; 2714 data.mXPercentInView = xPercentInView; 2715 data.mYPercentInDoc = yPercentInDoc; 2716 data.mYPercentInView = yPercentInView; 2717 Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID, 2718 data).sendToTarget(); 2719 } 2720 } 2721 2722 // called by JNI 2723 private void centerFitRect(int x, int y, int width, int height) { 2724 if (mWebView == null) { 2725 return; 2726 } 2727 mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT, 2728 new Rect(x, y, x + width, y + height)).sendToTarget(); 2729 } 2730 2731 // called by JNI 2732 private void setScrollbarModes(int hMode, int vMode) { 2733 if (mWebView == null) { 2734 return; 2735 } 2736 mWebView.mPrivateHandler.obtainMessage(WebView.SET_SCROLLBAR_MODES, 2737 hMode, vMode).sendToTarget(); 2738 } 2739 2740 // called by JNI 2741 @SuppressWarnings("unused") 2742 private void selectAt(int x, int y) { 2743 if (mWebView != null) { 2744 mWebView.mPrivateHandler.obtainMessage(WebView.SELECT_AT, x, y).sendToTarget(); 2745 } 2746 } 2747 2748 private void useMockDeviceOrientation() { 2749 mDeviceMotionAndOrientationManager.useMock(); 2750 } 2751 2752 public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha, 2753 boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) { 2754 mDeviceMotionAndOrientationManager.setMockOrientation(canProvideAlpha, alpha, 2755 canProvideBeta, beta, canProvideGamma, gamma); 2756 } 2757 2758 protected DeviceMotionService getDeviceMotionService() { 2759 if (mDeviceMotionService == null) { 2760 mDeviceMotionService = 2761 new DeviceMotionService(mDeviceMotionAndOrientationManager, mContext); 2762 } 2763 return mDeviceMotionService; 2764 } 2765 2766 protected DeviceOrientationService getDeviceOrientationService() { 2767 if (mDeviceOrientationService == null) { 2768 mDeviceOrientationService = 2769 new DeviceOrientationService(mDeviceMotionAndOrientationManager, mContext); 2770 } 2771 return mDeviceOrientationService; 2772 } 2773 2774 private native void nativePause(); 2775 private native void nativeResume(); 2776 private native void nativeFreeMemory(); 2777 private native void nativeFullScreenPluginHidden(int npp); 2778 private native boolean nativeValidNodeAndBounds(int frame, int node, 2779 Rect bounds); 2780 2781 private native ArrayList<Rect> nativeGetTouchHighlightRects(int x, int y, 2782 int slop); 2783 2784 private native void nativeAutoFillForm(int queryId); 2785 private native void nativeScrollLayer(int layer, Rect rect); 2786} 2787