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