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