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