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