WebViewCore.java revision 51ef573a8336c637ac508c49b84b512d8f11fa3a
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.webkit; 18 19import android.content.Context; 20import android.content.Intent; 21import android.content.pm.PackageManager.NameNotFoundException; 22import android.database.Cursor; 23import android.graphics.Canvas; 24import android.graphics.DrawFilter; 25import android.graphics.Paint; 26import android.graphics.PaintFlagsDrawFilter; 27import android.graphics.Picture; 28import android.graphics.Point; 29import android.graphics.Rect; 30import android.graphics.Region; 31import android.net.Uri; 32import android.os.Handler; 33import android.os.Looper; 34import android.os.Message; 35import android.os.Process; 36import android.provider.Browser; 37import android.provider.OpenableColumns; 38import android.util.Log; 39import android.util.SparseBooleanArray; 40import android.view.KeyEvent; 41import android.view.SurfaceHolder; 42import android.view.SurfaceView; 43import android.view.View; 44import android.webkit.plugin.FullScreenDrawingModel; 45import android.webkit.plugin.SurfaceDrawingModel; 46import android.webkit.plugin.WebkitPlugin; 47 48import java.util.ArrayList; 49import java.util.Collection; 50import java.util.Map; 51import java.util.Set; 52 53import junit.framework.Assert; 54 55final class WebViewCore { 56 57 private static final String LOGTAG = "webcore"; 58 59 static { 60 // Load libwebcore during static initialization. This happens in the 61 // zygote process so it will be shared read-only across all app 62 // processes. 63 System.loadLibrary("webcore"); 64 } 65 66 /* 67 * WebViewCore always executes in the same thread as the native webkit. 68 */ 69 70 // The WebView that corresponds to this WebViewCore. 71 private WebView mWebView; 72 // Proxy for handling callbacks from native code 73 private final CallbackProxy mCallbackProxy; 74 // Settings object for maintaining all settings 75 private final WebSettings mSettings; 76 // Context for initializing the BrowserFrame with the proper assets. 77 private final Context mContext; 78 // The pointer to a native view object. 79 private int mNativeClass; 80 // The BrowserFrame is an interface to the native Frame component. 81 private BrowserFrame mBrowserFrame; 82 // Custom JS interfaces to add during the initialization. 83 private Map<String, Object> mJavascriptInterfaces; 84 /* 85 * range is from 200 to 10,000. 0 is a special value means device-width. -1 86 * means undefined. 87 */ 88 private int mViewportWidth = -1; 89 90 /* 91 * range is from 200 to 10,000. 0 is a special value means device-height. -1 92 * means undefined. 93 */ 94 private int mViewportHeight = -1; 95 96 /* 97 * scale in percent, range is from 1 to 1000. 0 means undefined. 98 */ 99 private int mViewportInitialScale = 0; 100 101 /* 102 * scale in percent, range is from 1 to 1000. 0 means undefined. 103 */ 104 private int mViewportMinimumScale = 0; 105 106 /* 107 * scale in percent, range is from 1 to 1000. 0 means undefined. 108 */ 109 private int mViewportMaximumScale = 0; 110 111 private boolean mViewportUserScalable = true; 112 113 /* 114 * range is from 70 to 400. 115 * 0 is a special value means device-dpi. The default scale factor will be 116 * always 100. 117 * -1 means undefined. The default scale factor will be 118 * WebView.DEFAULT_SCALE_PERCENT. 119 */ 120 private int mViewportDensityDpi = -1; 121 122 private int mRestoredScale = 0; 123 private int mRestoredScreenWidthScale = 0; 124 private int mRestoredX = 0; 125 private int mRestoredY = 0; 126 127 private int mWebkitScrollX = 0; 128 private int mWebkitScrollY = 0; 129 130 // The thread name used to identify the WebCore thread and for use in 131 // debugging other classes that require operation within the WebCore thread. 132 /* package */ static final String THREAD_NAME = "WebViewCoreThread"; 133 134 public WebViewCore(Context context, WebView w, CallbackProxy proxy, 135 Map<String, Object> javascriptInterfaces) { 136 // No need to assign this in the WebCore thread. 137 mCallbackProxy = proxy; 138 mWebView = w; 139 mJavascriptInterfaces = javascriptInterfaces; 140 // This context object is used to initialize the WebViewCore during 141 // subwindow creation. 142 mContext = context; 143 144 // We need to wait for the initial thread creation before sending 145 // a message to the WebCore thread. 146 // XXX: This is the only time the UI thread will wait for the WebCore 147 // thread! 148 synchronized (WebViewCore.class) { 149 if (sWebCoreHandler == null) { 150 // Create a global thread and start it. 151 Thread t = new Thread(new WebCoreThread()); 152 t.setName(THREAD_NAME); 153 t.start(); 154 try { 155 WebViewCore.class.wait(); 156 } catch (InterruptedException e) { 157 Log.e(LOGTAG, "Caught exception while waiting for thread " + 158 "creation."); 159 Log.e(LOGTAG, Log.getStackTraceString(e)); 160 } 161 } 162 } 163 // Create an EventHub to handle messages before and after the thread is 164 // ready. 165 mEventHub = new EventHub(); 166 // Create a WebSettings object for maintaining all settings 167 mSettings = new WebSettings(mContext, mWebView); 168 // The WebIconDatabase needs to be initialized within the UI thread so 169 // just request the instance here. 170 WebIconDatabase.getInstance(); 171 // Create the WebStorage singleton and the UI handler 172 WebStorage.getInstance().createUIHandler(); 173 // Create the UI handler for GeolocationPermissions 174 GeolocationPermissions.getInstance().createUIHandler(); 175 // Send a message to initialize the WebViewCore. 176 Message init = sWebCoreHandler.obtainMessage( 177 WebCoreThread.INITIALIZE, this); 178 sWebCoreHandler.sendMessage(init); 179 } 180 181 /* Initialize private data within the WebCore thread. 182 */ 183 private void initialize() { 184 /* Initialize our private BrowserFrame class to handle all 185 * frame-related functions. We need to create a new view which 186 * in turn creates a C level FrameView and attaches it to the frame. 187 */ 188 mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy, 189 mSettings, mJavascriptInterfaces); 190 mJavascriptInterfaces = null; 191 // Sync the native settings and also create the WebCore thread handler. 192 mSettings.syncSettingsAndCreateHandler(mBrowserFrame); 193 // Create the handler and transfer messages for the IconDatabase 194 WebIconDatabase.getInstance().createHandler(); 195 // Create the handler for WebStorage 196 WebStorage.getInstance().createHandler(); 197 // Create the handler for GeolocationPermissions. 198 GeolocationPermissions.getInstance().createHandler(); 199 // The transferMessages call will transfer all pending messages to the 200 // WebCore thread handler. 201 mEventHub.transferMessages(); 202 203 // Send a message back to WebView to tell it that we have set up the 204 // WebCore thread. 205 if (mWebView != null) { 206 Message.obtain(mWebView.mPrivateHandler, 207 WebView.WEBCORE_INITIALIZED_MSG_ID, 208 mNativeClass, 0).sendToTarget(); 209 } 210 211 } 212 213 /* Handle the initialization of WebViewCore during subwindow creation. This 214 * method is called from the WebCore thread but it is called before the 215 * INITIALIZE message can be handled. 216 */ 217 /* package */ void initializeSubwindow() { 218 // Go ahead and initialize the core components. 219 initialize(); 220 // Remove the INITIALIZE method so we don't try to initialize twice. 221 sWebCoreHandler.removeMessages(WebCoreThread.INITIALIZE, this); 222 } 223 224 /* Get the BrowserFrame component. This is used for subwindow creation and 225 * is called only from BrowserFrame in the WebCore thread. */ 226 /* package */ BrowserFrame getBrowserFrame() { 227 return mBrowserFrame; 228 } 229 230 //------------------------------------------------------------------------- 231 // Common methods 232 //------------------------------------------------------------------------- 233 234 /** 235 * Causes all timers to pause. This applies to all WebViews in the current 236 * app process. 237 */ 238 public static void pauseTimers() { 239 if (BrowserFrame.sJavaBridge == null) { 240 throw new IllegalStateException( 241 "No WebView has been created in this process!"); 242 } 243 BrowserFrame.sJavaBridge.pause(); 244 } 245 246 /** 247 * Resume all timers. This applies to all WebViews in the current process. 248 */ 249 public static void resumeTimers() { 250 if (BrowserFrame.sJavaBridge == null) { 251 throw new IllegalStateException( 252 "No WebView has been created in this process!"); 253 } 254 BrowserFrame.sJavaBridge.resume(); 255 } 256 257 public WebSettings getSettings() { 258 return mSettings; 259 } 260 261 /** 262 * Add an error message to the client's console. 263 * @param message The message to add 264 * @param lineNumber the line on which the error occurred 265 * @param sourceID the filename of the source that caused the error. 266 */ 267 protected void addMessageToConsole(String message, int lineNumber, String sourceID) { 268 mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID); 269 } 270 271 /** 272 * Invoke a javascript alert. 273 * @param message The message displayed in the alert. 274 */ 275 protected void jsAlert(String url, String message) { 276 mCallbackProxy.onJsAlert(url, message); 277 } 278 279 280 /** 281 * Called by JNI. Open a file chooser to upload a file. 282 * @return String version of the URI plus the name of the file. 283 * FIXME: Just return the URI here, and in FileSystem::pathGetFileName, call 284 * into Java to get the filename. 285 */ 286 private String openFileChooser() { 287 Uri uri = mCallbackProxy.openFileChooser(); 288 if (uri == null) return ""; 289 // Find out the name, and append it to the URI. 290 // Webkit will treat the name as the filename, and 291 // the URI as the path. The URI will be used 292 // in BrowserFrame to get the actual data. 293 Cursor cursor = mContext.getContentResolver().query( 294 uri, 295 new String[] { OpenableColumns.DISPLAY_NAME }, 296 null, 297 null, 298 null); 299 String name = ""; 300 if (cursor != null) { 301 try { 302 if (cursor.moveToNext()) { 303 name = cursor.getString(0); 304 } 305 } finally { 306 cursor.close(); 307 } 308 } 309 return uri.toString() + "/" + name; 310 } 311 312 /** 313 * Notify the browser that the origin has exceeded it's database quota. 314 * @param url The URL that caused the overflow. 315 * @param databaseIdentifier The identifier of the database. 316 * @param currentQuota The current quota for the origin. 317 * @param estimatedSize The estimated size of the database. 318 */ 319 protected void exceededDatabaseQuota(String url, 320 String databaseIdentifier, 321 long currentQuota, 322 long estimatedSize) { 323 // Inform the callback proxy of the quota overflow. Send an object 324 // that encapsulates a call to the nativeSetDatabaseQuota method to 325 // awaken the sleeping webcore thread when a decision from the 326 // client to allow or deny quota is available. 327 mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier, 328 currentQuota, estimatedSize, getUsedQuota(), 329 new WebStorage.QuotaUpdater() { 330 public void updateQuota(long quota) { 331 nativeSetNewStorageLimit(quota); 332 } 333 }); 334 } 335 336 /** 337 * Notify the browser that the appcache has exceeded its max size. 338 * @param spaceNeeded is the amount of disk space that would be needed 339 * in order for the last appcache operation to succeed. 340 */ 341 protected void reachedMaxAppCacheSize(long spaceNeeded) { 342 mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(), 343 new WebStorage.QuotaUpdater() { 344 public void updateQuota(long quota) { 345 nativeSetNewStorageLimit(quota); 346 } 347 }); 348 } 349 350 protected void populateVisitedLinks() { 351 ValueCallback callback = new ValueCallback<String[]>() { 352 public void onReceiveValue(String[] value) { 353 sendMessage(EventHub.POPULATE_VISITED_LINKS, (Object)value); 354 } 355 }; 356 mCallbackProxy.getVisitedHistory(callback); 357 } 358 359 /** 360 * Shows a prompt to ask the user to set the Geolocation permission state 361 * for the given origin. 362 * @param origin The origin for which Geolocation permissions are 363 * requested. 364 */ 365 protected void geolocationPermissionsShowPrompt(String origin) { 366 mCallbackProxy.onGeolocationPermissionsShowPrompt(origin, 367 new GeolocationPermissions.Callback() { 368 public void invoke(String origin, boolean allow, boolean remember) { 369 GeolocationPermissionsData data = new GeolocationPermissionsData(); 370 data.mOrigin = origin; 371 data.mAllow = allow; 372 data.mRemember = remember; 373 // Marshall to WebCore thread. 374 sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data); 375 } 376 }); 377 } 378 379 /** 380 * Hides the Geolocation permissions prompt. 381 */ 382 protected void geolocationPermissionsHidePrompt() { 383 mCallbackProxy.onGeolocationPermissionsHidePrompt(); 384 } 385 386 /** 387 * Invoke a javascript confirm dialog. 388 * @param message The message displayed in the dialog. 389 * @return True if the user confirmed or false if the user cancelled. 390 */ 391 protected boolean jsConfirm(String url, String message) { 392 return mCallbackProxy.onJsConfirm(url, message); 393 } 394 395 /** 396 * Invoke a javascript prompt dialog. 397 * @param message The message to be displayed in the dialog. 398 * @param defaultValue The default value in the prompt input. 399 * @return The input from the user or null to indicate the user cancelled 400 * the dialog. 401 */ 402 protected String jsPrompt(String url, String message, String defaultValue) { 403 return mCallbackProxy.onJsPrompt(url, message, defaultValue); 404 } 405 406 /** 407 * Invoke a javascript before unload dialog. 408 * @param url The url that is requesting the dialog. 409 * @param message The message displayed in the dialog. 410 * @return True if the user confirmed or false if the user cancelled. False 411 * will cancel the navigation. 412 */ 413 protected boolean jsUnload(String url, String message) { 414 return mCallbackProxy.onJsBeforeUnload(url, message); 415 } 416 417 /** 418 * 419 * Callback to notify that a JavaScript execution timeout has occured. 420 * @return True if the JavaScript execution should be interrupted. False 421 * will continue the execution. 422 */ 423 protected boolean jsInterrupt() { 424 return mCallbackProxy.onJsTimeout(); 425 } 426 427 //------------------------------------------------------------------------- 428 // JNI methods 429 //------------------------------------------------------------------------- 430 431 static native String nativeFindAddress(String addr, boolean caseInsensitive); 432 433 /** 434 * Empty the picture set. 435 */ 436 private native void nativeClearContent(); 437 438 /** 439 * Create a flat picture from the set of pictures. 440 */ 441 private native void nativeCopyContentToPicture(Picture picture); 442 443 /** 444 * Draw the picture set with a background color. Returns true 445 * if some individual picture took too long to draw and can be 446 * split into parts. Called from the UI thread. 447 */ 448 private native boolean nativeDrawContent(Canvas canvas, int color); 449 450 /** 451 * check to see if picture is blank and in progress 452 */ 453 private native boolean nativePictureReady(); 454 455 /** 456 * Redraw a portion of the picture set. The Point wh returns the 457 * width and height of the overall picture. 458 */ 459 private native boolean nativeRecordContent(Region invalRegion, Point wh); 460 461 private native boolean nativeFocusBoundsChanged(); 462 463 /** 464 * Splits slow parts of the picture set. Called from the webkit 465 * thread after nativeDrawContent returns true. 466 */ 467 private native void nativeSplitContent(); 468 469 private native boolean nativeKey(int keyCode, int unichar, 470 int repeatCount, boolean isShift, boolean isAlt, boolean isSym, 471 boolean isDown); 472 473 private native void nativeClick(int framePtr, int nodePtr); 474 475 private native void nativeSendListBoxChoices(boolean[] choices, int size); 476 477 private native void nativeSendListBoxChoice(int choice); 478 479 /* Tell webkit what its width and height are, for the purposes 480 of layout/line-breaking. These coordinates are in document space, 481 which is the same as View coords unless we have zoomed the document 482 (see nativeSetZoom). 483 screenWidth is used by layout to wrap column around. If viewport uses 484 fixed size, screenWidth can be different from width with zooming. 485 should this be called nativeSetViewPortSize? 486 */ 487 private native void nativeSetSize(int width, int height, int screenWidth, 488 float scale, int realScreenWidth, int screenHeight, 489 boolean ignoreHeight); 490 491 private native int nativeGetContentMinPrefWidth(); 492 493 // Start: functions that deal with text editing 494 private native void nativeReplaceTextfieldText( 495 int oldStart, int oldEnd, String replace, int newStart, int newEnd, 496 int textGeneration); 497 498 private native void passToJs(int gen, 499 String currentText, int keyCode, int keyValue, boolean down, 500 boolean cap, boolean fn, boolean sym); 501 502 private native void nativeSetFocusControllerActive(boolean active); 503 504 private native void nativeSaveDocumentState(int frame); 505 506 private native void nativeMoveMouse(int framePtr, int x, int y); 507 508 private native void nativeMoveMouseIfLatest(int moveGeneration, 509 int framePtr, int x, int y); 510 511 private native String nativeRetrieveHref(int framePtr, int nodePtr); 512 private native String nativeRetrieveAnchorText(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 hrefMsg.getData().putString("url", 1168 nativeRetrieveHref(msg.arg1, msg.arg2)); 1169 hrefMsg.getData().putString("title", 1170 nativeRetrieveAnchorText(msg.arg1, msg.arg2)); 1171 hrefMsg.sendToTarget(); 1172 break; 1173 } 1174 1175 case UPDATE_CACHE_AND_TEXT_ENTRY: 1176 nativeUpdateFrameCache(); 1177 // FIXME: this should provide a minimal rectangle 1178 if (mWebView != null) { 1179 mWebView.postInvalidate(); 1180 } 1181 sendUpdateTextEntry(); 1182 break; 1183 1184 case DOC_HAS_IMAGES: 1185 Message imageResult = (Message) msg.obj; 1186 imageResult.arg1 = 1187 mBrowserFrame.documentHasImages() ? 1 : 0; 1188 imageResult.sendToTarget(); 1189 break; 1190 1191 case DELETE_SELECTION: 1192 TextSelectionData deleteSelectionData 1193 = (TextSelectionData) msg.obj; 1194 nativeDeleteSelection(deleteSelectionData.mStart, 1195 deleteSelectionData.mEnd, msg.arg1); 1196 break; 1197 1198 case SET_SELECTION: 1199 nativeSetSelection(msg.arg1, msg.arg2); 1200 break; 1201 1202 case LISTBOX_CHOICES: 1203 SparseBooleanArray choices = (SparseBooleanArray) 1204 msg.obj; 1205 int choicesSize = msg.arg1; 1206 boolean[] choicesArray = new boolean[choicesSize]; 1207 for (int c = 0; c < choicesSize; c++) { 1208 choicesArray[c] = choices.get(c); 1209 } 1210 nativeSendListBoxChoices(choicesArray, 1211 choicesSize); 1212 break; 1213 1214 case SINGLE_LISTBOX_CHOICE: 1215 nativeSendListBoxChoice(msg.arg1); 1216 break; 1217 1218 case SET_BACKGROUND_COLOR: 1219 nativeSetBackgroundColor(msg.arg1); 1220 break; 1221 1222 case GET_SELECTION: 1223 String str = nativeGetSelection((Region) msg.obj); 1224 Message.obtain(mWebView.mPrivateHandler 1225 , WebView.UPDATE_CLIPBOARD, str) 1226 .sendToTarget(); 1227 break; 1228 1229 case DUMP_DOMTREE: 1230 nativeDumpDomTree(msg.arg1 == 1); 1231 break; 1232 1233 case DUMP_RENDERTREE: 1234 nativeDumpRenderTree(msg.arg1 == 1); 1235 break; 1236 1237 case DUMP_NAVTREE: 1238 nativeDumpNavTree(); 1239 break; 1240 1241 case SET_JS_FLAGS: 1242 nativeSetJsFlags((String)msg.obj); 1243 break; 1244 1245 case GEOLOCATION_PERMISSIONS_PROVIDE: 1246 GeolocationPermissionsData data = 1247 (GeolocationPermissionsData) msg.obj; 1248 nativeGeolocationPermissionsProvide(data.mOrigin, 1249 data.mAllow, data.mRemember); 1250 break; 1251 1252 case SYNC_SCROLL: 1253 mWebkitScrollX = msg.arg1; 1254 mWebkitScrollY = msg.arg2; 1255 break; 1256 1257 case SPLIT_PICTURE_SET: 1258 nativeSplitContent(); 1259 mSplitPictureIsScheduled = false; 1260 break; 1261 1262 case CLEAR_CONTENT: 1263 // Clear the view so that onDraw() will draw nothing 1264 // but white background 1265 // (See public method WebView.clearView) 1266 nativeClearContent(); 1267 break; 1268 1269 case MESSAGE_RELAY: 1270 if (msg.obj instanceof Message) { 1271 ((Message) msg.obj).sendToTarget(); 1272 } 1273 break; 1274 1275 case POPULATE_VISITED_LINKS: 1276 nativeProvideVisitedHistory((String[])msg.obj); 1277 break; 1278 } 1279 } 1280 }; 1281 // Take all queued messages and resend them to the new handler. 1282 synchronized (this) { 1283 int size = mMessages.size(); 1284 for (int i = 0; i < size; i++) { 1285 mHandler.sendMessage(mMessages.get(i)); 1286 } 1287 mMessages = null; 1288 } 1289 } 1290 1291 /** 1292 * Send a message internally to the queue or to the handler 1293 */ 1294 private synchronized void sendMessage(Message msg) { 1295 if (mBlockMessages) { 1296 return; 1297 } 1298 if (mMessages != null) { 1299 mMessages.add(msg); 1300 } else { 1301 mHandler.sendMessage(msg); 1302 } 1303 } 1304 1305 private synchronized void removeMessages(int what) { 1306 if (mBlockMessages) { 1307 return; 1308 } 1309 if (what == EventHub.WEBKIT_DRAW) { 1310 mDrawIsScheduled = false; 1311 } 1312 if (mMessages != null) { 1313 Log.w(LOGTAG, "Not supported in this case."); 1314 } else { 1315 mHandler.removeMessages(what); 1316 } 1317 } 1318 1319 private synchronized boolean hasMessages(int what) { 1320 if (mBlockMessages) { 1321 return false; 1322 } 1323 if (mMessages != null) { 1324 Log.w(LOGTAG, "hasMessages() is not supported in this case."); 1325 return false; 1326 } else { 1327 return mHandler.hasMessages(what); 1328 } 1329 } 1330 1331 private synchronized void sendMessageDelayed(Message msg, long delay) { 1332 if (mBlockMessages) { 1333 return; 1334 } 1335 mHandler.sendMessageDelayed(msg, delay); 1336 } 1337 1338 /** 1339 * Send a message internally to the front of the queue. 1340 */ 1341 private synchronized void sendMessageAtFrontOfQueue(Message msg) { 1342 if (mBlockMessages) { 1343 return; 1344 } 1345 if (mMessages != null) { 1346 mMessages.add(0, msg); 1347 } else { 1348 mHandler.sendMessageAtFrontOfQueue(msg); 1349 } 1350 } 1351 1352 /** 1353 * Remove all the messages. 1354 */ 1355 private synchronized void removeMessages() { 1356 // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed 1357 mDrawIsScheduled = false; 1358 mSplitPictureIsScheduled = false; 1359 if (mMessages != null) { 1360 mMessages.clear(); 1361 } else { 1362 mHandler.removeCallbacksAndMessages(null); 1363 } 1364 } 1365 1366 /** 1367 * Block sending messages to the EventHub. 1368 */ 1369 private synchronized void blockMessages() { 1370 mBlockMessages = true; 1371 } 1372 } 1373 1374 //------------------------------------------------------------------------- 1375 // Methods called by host activity (in the same thread) 1376 //------------------------------------------------------------------------- 1377 1378 void stopLoading() { 1379 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading"); 1380 if (mBrowserFrame != null) { 1381 mBrowserFrame.stopLoading(); 1382 } 1383 } 1384 1385 //------------------------------------------------------------------------- 1386 // Methods called by WebView 1387 // If it refers to local variable, it needs synchronized(). 1388 // If it needs WebCore, it has to send message. 1389 //------------------------------------------------------------------------- 1390 1391 void sendMessage(Message msg) { 1392 mEventHub.sendMessage(msg); 1393 } 1394 1395 void sendMessage(int what) { 1396 mEventHub.sendMessage(Message.obtain(null, what)); 1397 } 1398 1399 void sendMessage(int what, Object obj) { 1400 mEventHub.sendMessage(Message.obtain(null, what, obj)); 1401 } 1402 1403 void sendMessage(int what, int arg1) { 1404 // just ignore the second argument (make it 0) 1405 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0)); 1406 } 1407 1408 void sendMessage(int what, int arg1, int arg2) { 1409 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2)); 1410 } 1411 1412 void sendMessage(int what, int arg1, Object obj) { 1413 // just ignore the second argument (make it 0) 1414 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj)); 1415 } 1416 1417 void sendMessage(int what, int arg1, int arg2, Object obj) { 1418 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj)); 1419 } 1420 1421 void sendMessageDelayed(int what, Object obj, long delay) { 1422 mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay); 1423 } 1424 1425 void removeMessages(int what) { 1426 mEventHub.removeMessages(what); 1427 } 1428 1429 void removeMessages() { 1430 mEventHub.removeMessages(); 1431 } 1432 1433 /** 1434 * Removes pending messages and trigger a DESTROY message to send to 1435 * WebCore. 1436 * Called from UI thread. 1437 */ 1438 void destroy() { 1439 // We don't want anyone to post a message between removing pending 1440 // messages and sending the destroy message. 1441 synchronized (mEventHub) { 1442 // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to 1443 // be preserved even the WebView is destroyed. 1444 // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS 1445 boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS); 1446 boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS); 1447 mEventHub.removeMessages(); 1448 mEventHub.sendMessageAtFrontOfQueue( 1449 Message.obtain(null, EventHub.DESTROY)); 1450 if (hasPause) { 1451 mEventHub.sendMessageAtFrontOfQueue( 1452 Message.obtain(null, EventHub.PAUSE_TIMERS)); 1453 } 1454 if (hasResume) { 1455 mEventHub.sendMessageAtFrontOfQueue( 1456 Message.obtain(null, EventHub.RESUME_TIMERS)); 1457 } 1458 mEventHub.blockMessages(); 1459 } 1460 } 1461 1462 //------------------------------------------------------------------------- 1463 // WebViewCore private methods 1464 //------------------------------------------------------------------------- 1465 1466 private void clearCache(boolean includeDiskFiles) { 1467 mBrowserFrame.clearCache(); 1468 if (includeDiskFiles) { 1469 CacheManager.removeAllCacheFiles(); 1470 } 1471 } 1472 1473 private void loadUrl(String url) { 1474 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url); 1475 mBrowserFrame.loadUrl(url); 1476 } 1477 1478 private void key(KeyEvent evt, boolean isDown) { 1479 if (DebugFlags.WEB_VIEW_CORE) { 1480 Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", " 1481 + evt); 1482 } 1483 int keyCode = evt.getKeyCode(); 1484 if (!nativeKey(keyCode, evt.getUnicodeChar(), 1485 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(), 1486 evt.isSymPressed(), 1487 isDown) && keyCode != KeyEvent.KEYCODE_ENTER) { 1488 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP 1489 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { 1490 if (DebugFlags.WEB_VIEW_CORE) { 1491 Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode); 1492 } 1493 if (mWebView != null && evt.isDown()) { 1494 Message.obtain(mWebView.mPrivateHandler, 1495 WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget(); 1496 } 1497 return; 1498 } 1499 // bubble up the event handling 1500 // but do not bubble up the ENTER key, which would open the search 1501 // bar without any text. 1502 mCallbackProxy.onUnhandledKeyEvent(evt); 1503 } 1504 } 1505 1506 // These values are used to avoid requesting a layout based on old values 1507 private int mCurrentViewWidth = 0; 1508 private int mCurrentViewHeight = 0; 1509 private float mCurrentViewScale = 1.0f; 1510 1511 // notify webkit that our virtual view size changed size (after inv-zoom) 1512 private void viewSizeChanged(int w, int h, int textwrapWidth, float scale, 1513 boolean ignoreHeight) { 1514 if (DebugFlags.WEB_VIEW_CORE) { 1515 Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h 1516 + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale); 1517 } 1518 if (w == 0) { 1519 Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); 1520 return; 1521 } 1522 int width = w; 1523 if (mSettings.getUseWideViewPort()) { 1524 if (mViewportWidth == -1) { 1525 if (mSettings.getLayoutAlgorithm() == 1526 WebSettings.LayoutAlgorithm.NORMAL) { 1527 width = WebView.DEFAULT_VIEWPORT_WIDTH; 1528 } else { 1529 /* 1530 * if a page's minimum preferred width is wider than the 1531 * given "w", use it instead to get better layout result. If 1532 * we start a page with MAX_ZOOM_WIDTH, "w" will be always 1533 * wider. If we start a page with screen width, due to the 1534 * delay between {@link #didFirstLayout} and 1535 * {@link #viewSizeChanged}, 1536 * {@link #nativeGetContentMinPrefWidth} will return a more 1537 * accurate value than initial 0 to result a better layout. 1538 * In the worse case, the native width will be adjusted when 1539 * next zoom or screen orientation change happens. 1540 */ 1541 width = Math.min(WebView.sMaxViewportWidth, Math.max(w, 1542 Math.max(WebView.DEFAULT_VIEWPORT_WIDTH, 1543 nativeGetContentMinPrefWidth()))); 1544 } 1545 } else { 1546 width = Math.max(w, mViewportWidth); 1547 } 1548 } 1549 nativeSetSize(width, width == w ? h : Math.round((float) width * h / w), 1550 textwrapWidth, scale, w, h, ignoreHeight); 1551 // Remember the current width and height 1552 boolean needInvalidate = (mCurrentViewWidth == 0); 1553 mCurrentViewWidth = w; 1554 mCurrentViewHeight = h; 1555 mCurrentViewScale = scale; 1556 if (needInvalidate) { 1557 // ensure {@link #webkitDraw} is called as we were blocking in 1558 // {@link #contentDraw} when mCurrentViewWidth is 0 1559 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged"); 1560 contentDraw(); 1561 } 1562 mEventHub.sendMessage(Message.obtain(null, 1563 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY)); 1564 } 1565 1566 private void sendUpdateTextEntry() { 1567 if (mWebView != null) { 1568 Message.obtain(mWebView.mPrivateHandler, 1569 WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget(); 1570 } 1571 } 1572 1573 // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize 1574 // callbacks. Computes the sum of database quota for all origins. 1575 private long getUsedQuota() { 1576 WebStorage webStorage = WebStorage.getInstance(); 1577 Collection<WebStorage.Origin> origins = webStorage.getOriginsSync(); 1578 1579 if (origins == null) { 1580 return 0; 1581 } 1582 long usedQuota = 0; 1583 for (WebStorage.Origin website : origins) { 1584 usedQuota += website.getQuota(); 1585 } 1586 return usedQuota; 1587 } 1588 1589 // Used to avoid posting more than one draw message. 1590 private boolean mDrawIsScheduled; 1591 1592 // Used to avoid posting more than one split picture message. 1593 private boolean mSplitPictureIsScheduled; 1594 1595 // Used to suspend drawing. 1596 private boolean mDrawIsPaused; 1597 1598 // mRestoreState is set in didFirstLayout(), and reset in the next 1599 // webkitDraw after passing it to the UI thread. 1600 private RestoreState mRestoreState = null; 1601 1602 static class RestoreState { 1603 float mMinScale; 1604 float mMaxScale; 1605 float mViewScale; 1606 float mTextWrapScale; 1607 float mDefaultScale; 1608 int mScrollX; 1609 int mScrollY; 1610 boolean mMobileSite; 1611 } 1612 1613 static class DrawData { 1614 DrawData() { 1615 mInvalRegion = new Region(); 1616 mWidthHeight = new Point(); 1617 } 1618 Region mInvalRegion; 1619 Point mViewPoint; 1620 Point mWidthHeight; 1621 int mMinPrefWidth; 1622 RestoreState mRestoreState; // only non-null if it is for the first 1623 // picture set after the first layout 1624 boolean mFocusSizeChanged; 1625 } 1626 1627 private void webkitDraw() { 1628 mDrawIsScheduled = false; 1629 DrawData draw = new DrawData(); 1630 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); 1631 if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight) 1632 == false) { 1633 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort"); 1634 return; 1635 } 1636 if (mWebView != null) { 1637 // Send the native view size that was used during the most recent 1638 // layout. 1639 draw.mFocusSizeChanged = nativeFocusBoundsChanged(); 1640 draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight); 1641 if (mSettings.getUseWideViewPort()) { 1642 draw.mMinPrefWidth = Math.max( 1643 mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH 1644 : (mViewportWidth == 0 ? mCurrentViewWidth 1645 : mViewportWidth), 1646 nativeGetContentMinPrefWidth()); 1647 } 1648 if (mRestoreState != null) { 1649 draw.mRestoreState = mRestoreState; 1650 mRestoreState = null; 1651 } 1652 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); 1653 Message.obtain(mWebView.mPrivateHandler, 1654 WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); 1655 if (mWebkitScrollX != 0 || mWebkitScrollY != 0) { 1656 // as we have the new picture, try to sync the scroll position 1657 Message.obtain(mWebView.mPrivateHandler, 1658 WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX, 1659 mWebkitScrollY).sendToTarget(); 1660 mWebkitScrollX = mWebkitScrollY = 0; 1661 } 1662 } 1663 } 1664 1665 /////////////////////////////////////////////////////////////////////////// 1666 // These are called from the UI thread, not our thread 1667 1668 static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG | 1669 Paint.DITHER_FLAG | 1670 Paint.SUBPIXEL_TEXT_FLAG; 1671 static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG | 1672 Paint.DITHER_FLAG; 1673 1674 final DrawFilter mZoomFilter = 1675 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG); 1676 final DrawFilter mScrollFilter = null; 1677 // If we need to trade more speed for less quality on slower devices 1678 // use this: new PaintFlagsDrawFilter(SCROLL_BITS, 0); 1679 1680 /* package */ void drawContentPicture(Canvas canvas, int color, 1681 boolean animatingZoom, 1682 boolean animatingScroll) { 1683 DrawFilter df = null; 1684 if (animatingZoom) { 1685 df = mZoomFilter; 1686 } else if (animatingScroll) { 1687 df = mScrollFilter; 1688 } 1689 canvas.setDrawFilter(df); 1690 boolean tookTooLong = nativeDrawContent(canvas, color); 1691 canvas.setDrawFilter(null); 1692 if (tookTooLong && mSplitPictureIsScheduled == false) { 1693 mSplitPictureIsScheduled = true; 1694 sendMessage(EventHub.SPLIT_PICTURE_SET); 1695 } 1696 } 1697 1698 /* package */ synchronized boolean pictureReady() { 1699 return 0 != mNativeClass ? nativePictureReady() : false; 1700 } 1701 1702 /*package*/ synchronized Picture copyContentPicture() { 1703 Picture result = new Picture(); 1704 if (0 != mNativeClass) { 1705 nativeCopyContentToPicture(result); 1706 } 1707 return result; 1708 } 1709 1710 static void pauseUpdate(WebViewCore core) { 1711 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1712 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1713 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1714 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1715 .obtainMessage(WebCoreThread.REDUCE_PRIORITY)); 1716 // Note: there is one possible failure mode. If pauseUpdate() is called 1717 // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out 1718 // of the queue and about to be executed. mDrawIsScheduled may be set to 1719 // false in webkitDraw(). So update won't be blocked. But at least the 1720 // webcore thread priority is still lowered. 1721 if (core != null) { 1722 synchronized (core) { 1723 core.mDrawIsPaused = true; 1724 core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW); 1725 } 1726 } 1727 } 1728 1729 static void resumeUpdate(WebViewCore core) { 1730 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1731 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1732 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1733 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1734 .obtainMessage(WebCoreThread.RESUME_PRIORITY)); 1735 if (core != null) { 1736 synchronized (core) { 1737 core.mDrawIsScheduled = false; 1738 core.mDrawIsPaused = false; 1739 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate"); 1740 core.contentDraw(); 1741 } 1742 } 1743 } 1744 1745 static void startCacheTransaction() { 1746 sWebCoreHandler.sendMessage(sWebCoreHandler 1747 .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER)); 1748 } 1749 1750 static void endCacheTransaction() { 1751 sWebCoreHandler.sendMessage(sWebCoreHandler 1752 .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER)); 1753 } 1754 1755 ////////////////////////////////////////////////////////////////////////// 1756 1757 private void restoreState(int index) { 1758 WebBackForwardList list = mCallbackProxy.getBackForwardList(); 1759 int size = list.getSize(); 1760 for (int i = 0; i < size; i++) { 1761 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); 1762 } 1763 mBrowserFrame.mLoadInitFromJava = true; 1764 list.restoreIndex(mBrowserFrame.mNativeFrame, index); 1765 mBrowserFrame.mLoadInitFromJava = false; 1766 } 1767 1768 //------------------------------------------------------------------------- 1769 // Implement abstract methods in WebViewCore, native WebKit callback part 1770 //------------------------------------------------------------------------- 1771 1772 // called from JNI or WebView thread 1773 /* package */ void contentDraw() { 1774 // don't update the Picture until we have an initial width and finish 1775 // the first layout 1776 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { 1777 return; 1778 } 1779 // only fire an event if this is our first request 1780 synchronized (this) { 1781 if (mDrawIsPaused || mDrawIsScheduled) { 1782 return; 1783 } 1784 mDrawIsScheduled = true; 1785 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 1786 } 1787 } 1788 1789 // called by JNI 1790 private void contentScrollBy(int dx, int dy, boolean animate) { 1791 if (!mBrowserFrame.firstLayoutDone()) { 1792 // Will this happen? If yes, we need to do something here. 1793 return; 1794 } 1795 if (mWebView != null) { 1796 Message msg = Message.obtain(mWebView.mPrivateHandler, 1797 WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate)); 1798 if (mDrawIsScheduled) { 1799 mEventHub.sendMessage(Message.obtain(null, 1800 EventHub.MESSAGE_RELAY, msg)); 1801 } else { 1802 msg.sendToTarget(); 1803 } 1804 } 1805 } 1806 1807 // called by JNI 1808 private void contentScrollTo(int x, int y) { 1809 if (!mBrowserFrame.firstLayoutDone()) { 1810 /* 1811 * WebKit restore state will be called before didFirstLayout(), 1812 * remember the position as it has to be applied after restoring 1813 * zoom factor which is controlled by screenWidth. 1814 */ 1815 mRestoredX = x; 1816 mRestoredY = y; 1817 return; 1818 } 1819 if (mWebView != null) { 1820 Message msg = Message.obtain(mWebView.mPrivateHandler, 1821 WebView.SCROLL_TO_MSG_ID, x, y); 1822 if (mDrawIsScheduled) { 1823 mEventHub.sendMessage(Message.obtain(null, 1824 EventHub.MESSAGE_RELAY, msg)); 1825 } else { 1826 msg.sendToTarget(); 1827 } 1828 } 1829 } 1830 1831 // called by JNI 1832 private void contentSpawnScrollTo(int x, int y) { 1833 if (!mBrowserFrame.firstLayoutDone()) { 1834 /* 1835 * WebKit restore state will be called before didFirstLayout(), 1836 * remember the position as it has to be applied after restoring 1837 * zoom factor which is controlled by screenWidth. 1838 */ 1839 mRestoredX = x; 1840 mRestoredY = y; 1841 return; 1842 } 1843 if (mWebView != null) { 1844 Message msg = Message.obtain(mWebView.mPrivateHandler, 1845 WebView.SPAWN_SCROLL_TO_MSG_ID, x, y); 1846 if (mDrawIsScheduled) { 1847 mEventHub.sendMessage(Message.obtain(null, 1848 EventHub.MESSAGE_RELAY, msg)); 1849 } else { 1850 msg.sendToTarget(); 1851 } 1852 } 1853 } 1854 1855 // called by JNI 1856 private void sendNotifyProgressFinished() { 1857 sendUpdateTextEntry(); 1858 // as CacheManager can behave based on database transaction, we need to 1859 // call tick() to trigger endTransaction 1860 sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER); 1861 sWebCoreHandler.sendMessage(sWebCoreHandler 1862 .obtainMessage(WebCoreThread.CACHE_TICKER)); 1863 contentDraw(); 1864 } 1865 1866 /* Called by JNI. The coordinates are in doc coordinates, so they need to 1867 be scaled before they can be used by the view system, which happens 1868 in WebView since it (and its thread) know the current scale factor. 1869 */ 1870 private void sendViewInvalidate(int left, int top, int right, int bottom) { 1871 if (mWebView != null) { 1872 Message.obtain(mWebView.mPrivateHandler, 1873 WebView.INVAL_RECT_MSG_ID, 1874 new Rect(left, top, right, bottom)).sendToTarget(); 1875 } 1876 } 1877 1878 /* package */ WebView getWebView() { 1879 return mWebView; 1880 } 1881 1882 private native void setViewportSettingsFromNative(); 1883 1884 // called by JNI 1885 private void didFirstLayout(boolean standardLoad) { 1886 if (DebugFlags.WEB_VIEW_CORE) { 1887 Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad); 1888 } 1889 1890 mBrowserFrame.didFirstLayout(); 1891 1892 if (mWebView == null) return; 1893 1894 setupViewport(standardLoad || mRestoredScale > 0); 1895 1896 // reset the scroll position, the restored offset and scales 1897 mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY 1898 = mRestoredScale = mRestoredScreenWidthScale = 0; 1899 } 1900 1901 // called by JNI 1902 private void updateViewport() { 1903 // if updateViewport is called before first layout, wait until first 1904 // layout to update the viewport. In the rare case, this is called after 1905 // first layout, force an update as we have just parsed the viewport 1906 // meta tag. 1907 if (mBrowserFrame.firstLayoutDone()) { 1908 setupViewport(true); 1909 } 1910 } 1911 1912 private void setupViewport(boolean updateRestoreState) { 1913 // set the viewport settings from WebKit 1914 setViewportSettingsFromNative(); 1915 1916 // adjust the default scale to match the densityDpi 1917 float adjust = 1.0f; 1918 if (mViewportDensityDpi == -1) { 1919 if (WebView.DEFAULT_SCALE_PERCENT != 100) { 1920 adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f; 1921 } 1922 } else if (mViewportDensityDpi > 0) { 1923 adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi 1924 / mViewportDensityDpi; 1925 } 1926 int defaultScale = (int) (adjust * 100); 1927 1928 if (mViewportInitialScale > 0) { 1929 mViewportInitialScale *= adjust; 1930 } 1931 if (mViewportMinimumScale > 0) { 1932 mViewportMinimumScale *= adjust; 1933 } 1934 if (mViewportMaximumScale > 0) { 1935 mViewportMaximumScale *= adjust; 1936 } 1937 1938 // infer the values if they are not defined. 1939 if (mViewportWidth == 0) { 1940 if (mViewportInitialScale == 0) { 1941 mViewportInitialScale = defaultScale; 1942 } 1943 } 1944 if (mViewportUserScalable == false) { 1945 mViewportInitialScale = defaultScale; 1946 mViewportMinimumScale = defaultScale; 1947 mViewportMaximumScale = defaultScale; 1948 } 1949 if (mViewportMinimumScale > mViewportInitialScale 1950 && mViewportInitialScale != 0) { 1951 mViewportMinimumScale = mViewportInitialScale; 1952 } 1953 if (mViewportMaximumScale > 0 1954 && mViewportMaximumScale < mViewportInitialScale) { 1955 mViewportMaximumScale = mViewportInitialScale; 1956 } 1957 if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) { 1958 mViewportWidth = 0; 1959 } 1960 1961 // if mViewportWidth is 0, it means device-width, always update. 1962 if (mViewportWidth != 0 && !updateRestoreState) return; 1963 1964 // now notify webview 1965 // webViewWidth refers to the width in the view system 1966 int webViewWidth; 1967 // viewportWidth refers to the width in the document system 1968 int viewportWidth = mCurrentViewWidth; 1969 if (viewportWidth == 0) { 1970 // this may happen when WebView just starts. This is not perfect as 1971 // we call WebView method from WebCore thread. But not perfect 1972 // reference is better than no reference. 1973 webViewWidth = mWebView.getViewWidth(); 1974 viewportWidth = (int) (webViewWidth / adjust); 1975 if (viewportWidth == 0) { 1976 Log.w(LOGTAG, "Can't get the viewWidth after the first layout"); 1977 } 1978 } else { 1979 webViewWidth = Math.round(viewportWidth * mCurrentViewScale); 1980 } 1981 mRestoreState = new RestoreState(); 1982 mRestoreState.mMinScale = mViewportMinimumScale / 100.0f; 1983 mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f; 1984 mRestoreState.mDefaultScale = adjust; 1985 mRestoreState.mScrollX = mRestoredX; 1986 mRestoreState.mScrollY = mRestoredY; 1987 mRestoreState.mMobileSite = (0 == mViewportWidth); 1988 if (mRestoredScale > 0) { 1989 if (mRestoredScreenWidthScale > 0) { 1990 mRestoreState.mTextWrapScale = 1991 mRestoredScreenWidthScale / 100.0f; 1992 // 0 will trigger WebView to turn on zoom overview mode 1993 mRestoreState.mViewScale = 0; 1994 } else { 1995 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 1996 mRestoredScale / 100.0f; 1997 } 1998 } else { 1999 if (mViewportInitialScale > 0) { 2000 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 2001 mViewportInitialScale / 100.0f; 2002 } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) { 2003 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 2004 (float) webViewWidth / mViewportWidth; 2005 } else { 2006 mRestoreState.mTextWrapScale = adjust; 2007 // 0 will trigger WebView to turn on zoom overview mode 2008 mRestoreState.mViewScale = 0; 2009 } 2010 } 2011 2012 if (mWebView.mHeightCanMeasure) { 2013 // Trick to ensure that the Picture has the exact height for the 2014 // content by forcing to layout with 0 height after the page is 2015 // ready, which is indicated by didFirstLayout. This is essential to 2016 // get rid of the white space in the GMail which uses WebView for 2017 // message view. 2018 mWebView.mLastHeightSent = 0; 2019 // Send a negative scale to indicate that WebCore should reuse 2020 // the current scale 2021 WebView.ViewSizeData data = new WebView.ViewSizeData(); 2022 data.mWidth = mWebView.mLastWidthSent; 2023 data.mHeight = 0; 2024 // if mHeightCanMeasure is true, getUseWideViewPort() can't be 2025 // true. It is safe to use mWidth for mTextWrapWidth. 2026 data.mTextWrapWidth = data.mWidth; 2027 data.mScale = -1.0f; 2028 data.mIgnoreHeight = false; 2029 // send VIEW_SIZE_CHANGED to the front of the queue so that we can 2030 // avoid pushing the wrong picture to the WebView side. If there is 2031 // a VIEW_SIZE_CHANGED in the queue, probably from WebView side, 2032 // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED 2033 // in the queue, as mLastHeightSent has been updated here, we may 2034 // miss the requestLayout in WebView side after the new picture. 2035 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2036 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 2037 EventHub.VIEW_SIZE_CHANGED, data)); 2038 } else if (mSettings.getUseWideViewPort()) { 2039 if (viewportWidth == 0) { 2040 // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView 2041 // to WebViewCore 2042 mWebView.mLastWidthSent = 0; 2043 } else { 2044 WebView.ViewSizeData data = new WebView.ViewSizeData(); 2045 // mViewScale as 0 means it is in zoom overview mode. So we don't 2046 // know the exact scale. If mRestoredScale is non-zero, use it; 2047 // otherwise just use mTextWrapScale as the initial scale. 2048 data.mScale = mRestoreState.mViewScale == 0 2049 ? (mRestoredScale > 0 ? mRestoredScale / 100.0f 2050 : mRestoreState.mTextWrapScale) 2051 : mRestoreState.mViewScale; 2052 if (DebugFlags.WEB_VIEW_CORE) { 2053 Log.v(LOGTAG, "setupViewport" 2054 + " mRestoredScale=" + mRestoredScale 2055 + " mViewScale=" + mRestoreState.mViewScale 2056 + " mTextWrapScale=" + mRestoreState.mTextWrapScale 2057 ); 2058 } 2059 data.mWidth = Math.round(webViewWidth / data.mScale); 2060 data.mHeight = mCurrentViewHeight * data.mWidth / viewportWidth; 2061 data.mTextWrapWidth = Math.round(webViewWidth 2062 / mRestoreState.mTextWrapScale); 2063 data.mIgnoreHeight = false; 2064 // send VIEW_SIZE_CHANGED to the front of the queue so that we 2065 // can avoid pushing the wrong picture to the WebView side. 2066 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2067 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 2068 EventHub.VIEW_SIZE_CHANGED, data)); 2069 } 2070 } 2071 } 2072 2073 // called by JNI 2074 private void restoreScale(int scale) { 2075 if (mBrowserFrame.firstLayoutDone() == false) { 2076 mRestoredScale = scale; 2077 } 2078 } 2079 2080 // called by JNI 2081 private void restoreScreenWidthScale(int scale) { 2082 if (!mSettings.getUseWideViewPort()) { 2083 return; 2084 } 2085 2086 if (mBrowserFrame.firstLayoutDone() == false) { 2087 mRestoredScreenWidthScale = scale; 2088 } 2089 } 2090 2091 // called by JNI 2092 private void needTouchEvents(boolean need) { 2093 if (mWebView != null) { 2094 Message.obtain(mWebView.mPrivateHandler, 2095 WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0) 2096 .sendToTarget(); 2097 } 2098 } 2099 2100 // called by JNI 2101 private void updateTextfield(int ptr, boolean changeToPassword, 2102 String text, int textGeneration) { 2103 if (mWebView != null) { 2104 Message msg = Message.obtain(mWebView.mPrivateHandler, 2105 WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 2106 textGeneration, text); 2107 msg.getData().putBoolean("password", changeToPassword); 2108 msg.sendToTarget(); 2109 } 2110 } 2111 2112 // called by JNI 2113 private void updateTextSelection(int pointer, int start, int end, 2114 int textGeneration) { 2115 if (mWebView != null) { 2116 Message.obtain(mWebView.mPrivateHandler, 2117 WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration, 2118 new TextSelectionData(start, end)).sendToTarget(); 2119 } 2120 } 2121 2122 // called by JNI 2123 private void clearTextEntry() { 2124 if (mWebView == null) return; 2125 Message.obtain(mWebView.mPrivateHandler, 2126 WebView.CLEAR_TEXT_ENTRY).sendToTarget(); 2127 } 2128 2129 // called by JNI 2130 private void sendFindAgain() { 2131 if (mWebView == null) return; 2132 Message.obtain(mWebView.mPrivateHandler, 2133 WebView.FIND_AGAIN).sendToTarget(); 2134 } 2135 2136 private native void nativeUpdateFrameCacheIfLoading(); 2137 2138 /** 2139 * Scroll the focused textfield to (xPercent, y) in document space 2140 */ 2141 private native void nativeScrollFocusedTextInput(float xPercent, int y); 2142 2143 // these must be in document space (i.e. not scaled/zoomed). 2144 private native void nativeSetScrollOffset(int gen, int dx, int dy); 2145 2146 private native void nativeSetGlobalBounds(int x, int y, int w, int h); 2147 2148 // called by JNI 2149 private void requestListBox(String[] array, int[] enabledArray, 2150 int[] selectedArray) { 2151 if (mWebView != null) { 2152 mWebView.requestListBox(array, enabledArray, selectedArray); 2153 } 2154 } 2155 2156 // called by JNI 2157 private void requestListBox(String[] array, int[] enabledArray, 2158 int selection) { 2159 if (mWebView != null) { 2160 mWebView.requestListBox(array, enabledArray, selection); 2161 } 2162 2163 } 2164 2165 // called by JNI 2166 private void requestKeyboard(boolean showKeyboard) { 2167 if (mWebView != null) { 2168 Message.obtain(mWebView.mPrivateHandler, 2169 WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0) 2170 .sendToTarget(); 2171 } 2172 } 2173 2174 // called by JNI 2175 private Class<?> getPluginClass(String libName, String clsName) { 2176 2177 if (mWebView == null) { 2178 return null; 2179 } 2180 2181 PluginManager pluginManager = PluginManager.getInstance(null); 2182 2183 String pkgName = pluginManager.getPluginsAPKName(libName); 2184 if (pkgName == null) { 2185 Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); 2186 return null; 2187 } 2188 2189 try { 2190 return pluginManager.getPluginClass(pkgName, clsName); 2191 } catch (NameNotFoundException e) { 2192 Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")"); 2193 } catch (ClassNotFoundException e) { 2194 Log.e(LOGTAG, "Unable to find plugin class (" + clsName + 2195 ") in the apk (" + pkgName + ")"); 2196 } 2197 2198 return null; 2199 } 2200 2201 private WebkitPlugin createPluginJavaInstance(String libName, int npp) { 2202 2203 if (mWebView == null) { 2204 return null; 2205 } 2206 2207 PluginManager pluginManager = PluginManager.getInstance(null); 2208 2209 String pkgName = pluginManager.getPluginsAPKName(libName); 2210 if (pkgName == null) { 2211 Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); 2212 return null; 2213 } 2214 2215 return pluginManager.getPluginInstance(pkgName, npp); 2216 } 2217 2218 // called by JNI. PluginWidget function to launch a full-screen view using a 2219 // View object provided by the plugin class. 2220 private void showFullScreenPlugin(WebkitPlugin webkitPlugin) { 2221 if (mWebView == null) { 2222 return; 2223 } 2224 2225 final FullScreenDrawingModel surface = webkitPlugin.getFullScreenSurface(); 2226 if(surface == null) { 2227 Log.e(LOGTAG, "Attempted to create an full-screen surface with a null drawing model"); 2228 return; 2229 } 2230 2231 WebChromeClient.CustomViewCallback callback = new WebChromeClient.CustomViewCallback() { 2232 public void onCustomViewHidden() { 2233 if (surface != null) { 2234 surface.onSurfaceRemoved(); 2235 } 2236 } 2237 }; 2238 2239 mCallbackProxy.showCustomView(surface.getSurface(), callback); 2240 } 2241 2242 private void hideFullScreenPlugin() { 2243 if (mWebView == null) { 2244 return; 2245 } 2246 2247 mCallbackProxy.hideCustomView(); 2248 } 2249 2250 // called by JNI. PluginWidget functions for creating an embedded View for 2251 // the surface drawing model. 2252 private ViewManager.ChildView createSurface(WebkitPlugin webkitPlugin, 2253 int x, int y, int width, int height) { 2254 2255 if (mWebView == null) { 2256 return null; 2257 } 2258 2259 SurfaceDrawingModel embeddedSurface = webkitPlugin.getEmbeddedSurface(); 2260 if(embeddedSurface == null) { 2261 Log.e(LOGTAG, "Attempted to create an embedded surface with a null drawing model"); 2262 return null; 2263 } 2264 2265 View pluginView = embeddedSurface.getSurface(); 2266 pluginView.setWillNotDraw(false); 2267 2268 ViewManager.ChildView view = mWebView.mViewManager.createView(); 2269 view.mView = pluginView; 2270 view.attachView(x, y, width, height); 2271 return view; 2272 } 2273 2274 private void updateSurface(ViewManager.ChildView childView, int x, int y, 2275 int width, int height) { 2276 childView.attachView(x, y, width, height); 2277 } 2278 2279 private void destroySurface(ViewManager.ChildView childView) { 2280 childView.removeView(); 2281 } 2282 2283 private native void nativePause(); 2284 private native void nativeResume(); 2285 private native void nativeFreeMemory(); 2286} 2287