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