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