WebViewCore.java revision 9a67c82089e43d37f5038c74b0e1dca8edc4ac8a
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 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 mNode = node; 665 mX = x; 666 mY = y; 667 } 668 int mMoveGeneration; 669 int mFrame; 670 int mNode; 671 int mX; 672 int mY; 673 } 674 675 static class JSInterfaceData { 676 Object mObject; 677 String mInterfaceName; 678 } 679 680 static class JSKeyData { 681 String mCurrentText; 682 KeyEvent mEvent; 683 } 684 685 static class MotionUpData { 686 int mFrame; 687 int mNode; 688 Rect mBounds; 689 int mX; 690 int mY; 691 } 692 693 static class PostUrlData { 694 String mUrl; 695 byte[] mPostData; 696 } 697 698 static class ReplaceTextData { 699 String mReplace; 700 int mNewStart; 701 int mNewEnd; 702 int mTextGeneration; 703 } 704 705 static class TextSelectionData { 706 public TextSelectionData(int start, int end) { 707 mStart = start; 708 mEnd = end; 709 } 710 int mStart; 711 int mEnd; 712 } 713 714 static class TouchUpData { 715 int mMoveGeneration; 716 int mFrame; 717 int mNode; 718 int mX; 719 int mY; 720 } 721 722 // mAction of TouchEventData can be MotionEvent.getAction() which uses the 723 // last two bytes or one of the following values 724 static final int ACTION_LONGPRESS = 0x100; 725 static final int ACTION_DOUBLETAP = 0x200; 726 727 static class TouchEventData { 728 int mAction; 729 int mX; 730 int mY; 731 } 732 733 static class GeolocationPermissionsData { 734 String mOrigin; 735 boolean mAllow; 736 boolean mRemember; 737 } 738 739 static class PluginFullScreenData { 740 View mView; 741 int mNpp; 742 int mDocX; 743 int mDocY; 744 int mDocWidth; 745 int mDocHeight; 746 } 747 748 static final String[] HandlerDebugString = { 749 "UPDATE_FRAME_CACHE_IF_LOADING", // = 98 750 "SCROLL_TEXT_INPUT", // = 99 751 "LOAD_URL", // = 100; 752 "STOP_LOADING", // = 101; 753 "RELOAD", // = 102; 754 "KEY_DOWN", // = 103; 755 "KEY_UP", // = 104; 756 "VIEW_SIZE_CHANGED", // = 105; 757 "GO_BACK_FORWARD", // = 106; 758 "SET_SCROLL_OFFSET", // = 107; 759 "RESTORE_STATE", // = 108; 760 "PAUSE_TIMERS", // = 109; 761 "RESUME_TIMERS", // = 110; 762 "CLEAR_CACHE", // = 111; 763 "CLEAR_HISTORY", // = 112; 764 "SET_SELECTION", // = 113; 765 "REPLACE_TEXT", // = 114; 766 "PASS_TO_JS", // = 115; 767 "SET_GLOBAL_BOUNDS", // = 116; 768 "UPDATE_CACHE_AND_TEXT_ENTRY", // = 117; 769 "CLICK", // = 118; 770 "SET_NETWORK_STATE", // = 119; 771 "DOC_HAS_IMAGES", // = 120; 772 "121", // = 121; 773 "DELETE_SELECTION", // = 122; 774 "LISTBOX_CHOICES", // = 123; 775 "SINGLE_LISTBOX_CHOICE", // = 124; 776 "MESSAGE_RELAY", // = 125; 777 "SET_BACKGROUND_COLOR", // = 126; 778 "SET_MOVE_FOCUS", // = 127 779 "SAVE_DOCUMENT_STATE", // = 128; 780 "GET_SELECTION", // = 129; 781 "WEBKIT_DRAW", // = 130; 782 "SYNC_SCROLL", // = 131; 783 "POST_URL", // = 132; 784 "SPLIT_PICTURE_SET", // = 133; 785 "CLEAR_CONTENT", // = 134; 786 "SET_MOVE_MOUSE", // = 135; 787 "SET_MOVE_MOUSE_IF_LATEST", // = 136; 788 "REQUEST_CURSOR_HREF", // = 137; 789 "ADD_JS_INTERFACE", // = 138; 790 "LOAD_DATA", // = 139; 791 "TOUCH_UP", // = 140; 792 "TOUCH_EVENT", // = 141; 793 "SET_ACTIVE", // = 142; 794 "ON_PAUSE", // = 143 795 "ON_RESUME", // = 144 796 "FREE_MEMORY", // = 145 797 "VALID_NODE_BOUNDS", // = 146 798 }; 799 800 class EventHub { 801 // Message Ids 802 static final int UPDATE_FRAME_CACHE_IF_LOADING = 98; 803 static final int SCROLL_TEXT_INPUT = 99; 804 static final int LOAD_URL = 100; 805 static final int STOP_LOADING = 101; 806 static final int RELOAD = 102; 807 static final int KEY_DOWN = 103; 808 static final int KEY_UP = 104; 809 static final int VIEW_SIZE_CHANGED = 105; 810 static final int GO_BACK_FORWARD = 106; 811 static final int SET_SCROLL_OFFSET = 107; 812 static final int RESTORE_STATE = 108; 813 static final int PAUSE_TIMERS = 109; 814 static final int RESUME_TIMERS = 110; 815 static final int CLEAR_CACHE = 111; 816 static final int CLEAR_HISTORY = 112; 817 static final int SET_SELECTION = 113; 818 static final int REPLACE_TEXT = 114; 819 static final int PASS_TO_JS = 115; 820 static final int SET_GLOBAL_BOUNDS = 116; 821 static final int UPDATE_CACHE_AND_TEXT_ENTRY = 117; 822 static final int CLICK = 118; 823 static final int SET_NETWORK_STATE = 119; 824 static final int DOC_HAS_IMAGES = 120; 825 static final int DELETE_SELECTION = 122; 826 static final int LISTBOX_CHOICES = 123; 827 static final int SINGLE_LISTBOX_CHOICE = 124; 828 static final int MESSAGE_RELAY = 125; 829 static final int SET_BACKGROUND_COLOR = 126; 830 static final int SET_MOVE_FOCUS = 127; 831 static final int SAVE_DOCUMENT_STATE = 128; 832 static final int GET_SELECTION = 129; 833 static final int WEBKIT_DRAW = 130; 834 static final int SYNC_SCROLL = 131; 835 static final int POST_URL = 132; 836 static final int SPLIT_PICTURE_SET = 133; 837 static final int CLEAR_CONTENT = 134; 838 839 // UI nav messages 840 static final int SET_MOVE_MOUSE = 135; 841 static final int SET_MOVE_MOUSE_IF_LATEST = 136; 842 static final int REQUEST_CURSOR_HREF = 137; 843 static final int ADD_JS_INTERFACE = 138; 844 static final int LOAD_DATA = 139; 845 846 // motion 847 static final int TOUCH_UP = 140; 848 // message used to pass UI touch events to WebCore 849 static final int TOUCH_EVENT = 141; 850 851 // Used to tell the focus controller not to draw the blinking cursor, 852 // based on whether the WebView has focus and whether the WebView's 853 // cursor matches the webpage's focus. 854 static final int SET_ACTIVE = 142; 855 856 // lifecycle activities for just this DOM (unlike pauseTimers, which 857 // is global) 858 static final int ON_PAUSE = 143; 859 static final int ON_RESUME = 144; 860 static final int FREE_MEMORY = 145; 861 static final int VALID_NODE_BOUNDS = 146; 862 863 // Network-based messaging 864 static final int CLEAR_SSL_PREF_TABLE = 150; 865 866 // Test harness messages 867 static final int REQUEST_EXT_REPRESENTATION = 160; 868 static final int REQUEST_DOC_AS_TEXT = 161; 869 870 // debugging 871 static final int DUMP_DOMTREE = 170; 872 static final int DUMP_RENDERTREE = 171; 873 static final int DUMP_NAVTREE = 172; 874 875 static final int SET_JS_FLAGS = 173; 876 // Geolocation 877 static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180; 878 879 static final int POPULATE_VISITED_LINKS = 181; 880 881 static final int HIDE_FULLSCREEN = 182; 882 883 // private message ids 884 private static final int DESTROY = 200; 885 886 // Private handler for WebCore messages. 887 private Handler mHandler; 888 // Message queue for containing messages before the WebCore thread is 889 // ready. 890 private ArrayList<Message> mMessages = new ArrayList<Message>(); 891 // Flag for blocking messages. This is used during DESTROY to avoid 892 // posting more messages to the EventHub or to WebView's event handler. 893 private boolean mBlockMessages; 894 895 private int mTid; 896 private int mSavedPriority; 897 898 /** 899 * Prevent other classes from creating an EventHub. 900 */ 901 private EventHub() {} 902 903 /** 904 * Transfer all messages to the newly created webcore thread handler. 905 */ 906 private void transferMessages() { 907 mTid = Process.myTid(); 908 mSavedPriority = Process.getThreadPriority(mTid); 909 910 mHandler = new Handler() { 911 @Override 912 public void handleMessage(Message msg) { 913 if (DebugFlags.WEB_VIEW_CORE) { 914 Log.v(LOGTAG, (msg.what < UPDATE_FRAME_CACHE_IF_LOADING 915 || msg.what 916 > VALID_NODE_BOUNDS ? Integer.toString(msg.what) 917 : HandlerDebugString[msg.what 918 - UPDATE_FRAME_CACHE_IF_LOADING]) 919 + " arg1=" + msg.arg1 + " arg2=" + msg.arg2 920 + " obj=" + msg.obj); 921 } 922 switch (msg.what) { 923 case WEBKIT_DRAW: 924 webkitDraw(); 925 break; 926 927 case DESTROY: 928 // Time to take down the world. Cancel all pending 929 // loads and destroy the native view and frame. 930 synchronized (WebViewCore.this) { 931 mBrowserFrame.destroy(); 932 mBrowserFrame = null; 933 mSettings.onDestroyed(); 934 mNativeClass = 0; 935 mWebView = null; 936 } 937 break; 938 939 case UPDATE_FRAME_CACHE_IF_LOADING: 940 nativeUpdateFrameCacheIfLoading(); 941 break; 942 943 case SCROLL_TEXT_INPUT: 944 nativeScrollFocusedTextInput( 945 ((Float) msg.obj).floatValue(), msg.arg1); 946 break; 947 948 case LOAD_URL: 949 loadUrl((String) msg.obj); 950 break; 951 952 case POST_URL: { 953 PostUrlData param = (PostUrlData) msg.obj; 954 mBrowserFrame.postUrl(param.mUrl, param.mPostData); 955 break; 956 } 957 case LOAD_DATA: 958 BaseUrlData loadParams = (BaseUrlData) msg.obj; 959 String baseUrl = loadParams.mBaseUrl; 960 if (baseUrl != null) { 961 int i = baseUrl.indexOf(':'); 962 if (i > 0) { 963 /* 964 * In 1.0, {@link 965 * WebView#loadDataWithBaseURL} can access 966 * local asset files as long as the data is 967 * valid. In the new WebKit, the restriction 968 * is tightened. To be compatible with 1.0, 969 * we automatically add the scheme of the 970 * baseUrl for local access as long as it is 971 * not http(s)/ftp(s)/about/javascript 972 */ 973 String scheme = baseUrl.substring(0, i); 974 if (!scheme.startsWith("http") && 975 !scheme.startsWith("ftp") && 976 !scheme.startsWith("about") && 977 !scheme.startsWith("javascript")) { 978 nativeRegisterURLSchemeAsLocal(scheme); 979 } 980 } 981 } 982 mBrowserFrame.loadData(baseUrl, 983 loadParams.mData, 984 loadParams.mMimeType, 985 loadParams.mEncoding, 986 loadParams.mFailUrl); 987 break; 988 989 case STOP_LOADING: 990 // If the WebCore has committed the load, but not 991 // finished the first layout yet, we need to set 992 // first layout done to trigger the interpreted side sync 993 // up with native side 994 if (mBrowserFrame.committed() 995 && !mBrowserFrame.firstLayoutDone()) { 996 mBrowserFrame.didFirstLayout(); 997 } 998 // Do this after syncing up the layout state. 999 stopLoading(); 1000 break; 1001 1002 case RELOAD: 1003 mBrowserFrame.reload(false); 1004 break; 1005 1006 case KEY_DOWN: 1007 key((KeyEvent) msg.obj, true); 1008 break; 1009 1010 case KEY_UP: 1011 key((KeyEvent) msg.obj, false); 1012 break; 1013 1014 case CLICK: 1015 nativeClick(msg.arg1, msg.arg2); 1016 break; 1017 1018 case VIEW_SIZE_CHANGED: { 1019 WebView.ViewSizeData data = 1020 (WebView.ViewSizeData) msg.obj; 1021 viewSizeChanged(data.mWidth, data.mHeight, 1022 data.mTextWrapWidth, data.mScale, 1023 data.mIgnoreHeight); 1024 break; 1025 } 1026 case SET_SCROLL_OFFSET: 1027 // note: these are in document coordinates 1028 // (inv-zoom) 1029 Point pt = (Point) msg.obj; 1030 nativeSetScrollOffset(msg.arg1, pt.x, pt.y); 1031 break; 1032 1033 case SET_GLOBAL_BOUNDS: 1034 Rect r = (Rect) msg.obj; 1035 nativeSetGlobalBounds(r.left, r.top, r.width(), 1036 r.height()); 1037 break; 1038 1039 case GO_BACK_FORWARD: 1040 // If it is a standard load and the load is not 1041 // committed yet, we interpret BACK as RELOAD 1042 if (!mBrowserFrame.committed() && msg.arg1 == -1 && 1043 (mBrowserFrame.loadType() == 1044 BrowserFrame.FRAME_LOADTYPE_STANDARD)) { 1045 mBrowserFrame.reload(true); 1046 } else { 1047 mBrowserFrame.goBackOrForward(msg.arg1); 1048 } 1049 break; 1050 1051 case RESTORE_STATE: 1052 stopLoading(); 1053 restoreState(msg.arg1); 1054 break; 1055 1056 case PAUSE_TIMERS: 1057 mSavedPriority = Process.getThreadPriority(mTid); 1058 Process.setThreadPriority(mTid, 1059 Process.THREAD_PRIORITY_BACKGROUND); 1060 pauseTimers(); 1061 if (CacheManager.disableTransaction()) { 1062 WebCoreThread.mCacheTickersBlocked = true; 1063 sWebCoreHandler.removeMessages( 1064 WebCoreThread.CACHE_TICKER); 1065 } 1066 break; 1067 1068 case RESUME_TIMERS: 1069 Process.setThreadPriority(mTid, mSavedPriority); 1070 resumeTimers(); 1071 if (CacheManager.enableTransaction()) { 1072 WebCoreThread.mCacheTickersBlocked = false; 1073 sWebCoreHandler.sendMessageDelayed( 1074 sWebCoreHandler.obtainMessage( 1075 WebCoreThread.CACHE_TICKER), 1076 WebCoreThread.CACHE_TICKER_INTERVAL); 1077 } 1078 break; 1079 1080 case ON_PAUSE: 1081 nativePause(); 1082 break; 1083 1084 case ON_RESUME: 1085 nativeResume(); 1086 break; 1087 1088 case FREE_MEMORY: 1089 clearCache(false); 1090 nativeFreeMemory(); 1091 break; 1092 1093 case SET_NETWORK_STATE: 1094 if (BrowserFrame.sJavaBridge == null) { 1095 throw new IllegalStateException("No WebView " + 1096 "has been created in this process!"); 1097 } 1098 BrowserFrame.sJavaBridge 1099 .setNetworkOnLine(msg.arg1 == 1); 1100 break; 1101 1102 case CLEAR_CACHE: 1103 clearCache(msg.arg1 == 1); 1104 break; 1105 1106 case CLEAR_HISTORY: 1107 mCallbackProxy.getBackForwardList(). 1108 close(mBrowserFrame.mNativeFrame); 1109 break; 1110 1111 case REPLACE_TEXT: 1112 ReplaceTextData rep = (ReplaceTextData) msg.obj; 1113 nativeReplaceTextfieldText(msg.arg1, msg.arg2, 1114 rep.mReplace, rep.mNewStart, rep.mNewEnd, 1115 rep.mTextGeneration); 1116 break; 1117 1118 case PASS_TO_JS: { 1119 JSKeyData jsData = (JSKeyData) msg.obj; 1120 KeyEvent evt = jsData.mEvent; 1121 int keyCode = evt.getKeyCode(); 1122 int keyValue = evt.getUnicodeChar(); 1123 int generation = msg.arg1; 1124 passToJs(generation, 1125 jsData.mCurrentText, 1126 keyCode, 1127 keyValue, 1128 evt.isDown(), 1129 evt.isShiftPressed(), evt.isAltPressed(), 1130 evt.isSymPressed()); 1131 break; 1132 } 1133 1134 case SAVE_DOCUMENT_STATE: { 1135 CursorData cDat = (CursorData) msg.obj; 1136 nativeSaveDocumentState(cDat.mFrame); 1137 break; 1138 } 1139 1140 case CLEAR_SSL_PREF_TABLE: 1141 Network.getInstance(mContext) 1142 .clearUserSslPrefTable(); 1143 break; 1144 1145 case TOUCH_UP: 1146 TouchUpData touchUpData = (TouchUpData) msg.obj; 1147 nativeTouchUp(touchUpData.mMoveGeneration, 1148 touchUpData.mFrame, touchUpData.mNode, 1149 touchUpData.mX, touchUpData.mY); 1150 break; 1151 1152 case TOUCH_EVENT: { 1153 TouchEventData ted = (TouchEventData) msg.obj; 1154 Message.obtain( 1155 mWebView.mPrivateHandler, 1156 WebView.PREVENT_TOUCH_ID, ted.mAction, 1157 nativeHandleTouchEvent(ted.mAction, ted.mX, 1158 ted.mY)).sendToTarget(); 1159 break; 1160 } 1161 1162 case SET_ACTIVE: 1163 nativeSetFocusControllerActive(msg.arg1 == 1); 1164 break; 1165 1166 case ADD_JS_INTERFACE: 1167 JSInterfaceData jsData = (JSInterfaceData) msg.obj; 1168 mBrowserFrame.addJavascriptInterface(jsData.mObject, 1169 jsData.mInterfaceName); 1170 break; 1171 1172 case REQUEST_EXT_REPRESENTATION: 1173 mBrowserFrame.externalRepresentation( 1174 (Message) msg.obj); 1175 break; 1176 1177 case REQUEST_DOC_AS_TEXT: 1178 mBrowserFrame.documentAsText((Message) msg.obj); 1179 break; 1180 1181 case SET_MOVE_FOCUS: 1182 CursorData focusData = (CursorData) msg.obj; 1183 nativeMoveFocus(focusData.mFrame, focusData.mNode); 1184 break; 1185 1186 case SET_MOVE_MOUSE: 1187 CursorData cursorData = (CursorData) msg.obj; 1188 nativeMoveMouse(cursorData.mFrame, 1189 cursorData.mX, cursorData.mY); 1190 break; 1191 1192 case SET_MOVE_MOUSE_IF_LATEST: 1193 CursorData cData = (CursorData) msg.obj; 1194 nativeMoveMouseIfLatest(cData.mMoveGeneration, 1195 cData.mFrame, 1196 cData.mX, cData.mY); 1197 break; 1198 1199 case REQUEST_CURSOR_HREF: { 1200 Message hrefMsg = (Message) msg.obj; 1201 hrefMsg.getData().putString("url", 1202 nativeRetrieveHref(msg.arg1, msg.arg2)); 1203 hrefMsg.getData().putString("title", 1204 nativeRetrieveAnchorText(msg.arg1, msg.arg2)); 1205 hrefMsg.sendToTarget(); 1206 break; 1207 } 1208 1209 case UPDATE_CACHE_AND_TEXT_ENTRY: 1210 nativeUpdateFrameCache(); 1211 // FIXME: this should provide a minimal rectangle 1212 if (mWebView != null) { 1213 mWebView.postInvalidate(); 1214 } 1215 sendUpdateTextEntry(); 1216 break; 1217 1218 case DOC_HAS_IMAGES: 1219 Message imageResult = (Message) msg.obj; 1220 imageResult.arg1 = 1221 mBrowserFrame.documentHasImages() ? 1 : 0; 1222 imageResult.sendToTarget(); 1223 break; 1224 1225 case DELETE_SELECTION: 1226 TextSelectionData deleteSelectionData 1227 = (TextSelectionData) msg.obj; 1228 nativeDeleteSelection(deleteSelectionData.mStart, 1229 deleteSelectionData.mEnd, msg.arg1); 1230 break; 1231 1232 case SET_SELECTION: 1233 nativeSetSelection(msg.arg1, msg.arg2); 1234 break; 1235 1236 case LISTBOX_CHOICES: 1237 SparseBooleanArray choices = (SparseBooleanArray) 1238 msg.obj; 1239 int choicesSize = msg.arg1; 1240 boolean[] choicesArray = new boolean[choicesSize]; 1241 for (int c = 0; c < choicesSize; c++) { 1242 choicesArray[c] = choices.get(c); 1243 } 1244 nativeSendListBoxChoices(choicesArray, 1245 choicesSize); 1246 break; 1247 1248 case SINGLE_LISTBOX_CHOICE: 1249 nativeSendListBoxChoice(msg.arg1); 1250 break; 1251 1252 case SET_BACKGROUND_COLOR: 1253 nativeSetBackgroundColor(msg.arg1); 1254 break; 1255 1256 case GET_SELECTION: 1257 String str = nativeGetSelection((Region) msg.obj); 1258 Message.obtain(mWebView.mPrivateHandler 1259 , WebView.UPDATE_CLIPBOARD, str) 1260 .sendToTarget(); 1261 break; 1262 1263 case DUMP_DOMTREE: 1264 nativeDumpDomTree(msg.arg1 == 1); 1265 break; 1266 1267 case DUMP_RENDERTREE: 1268 nativeDumpRenderTree(msg.arg1 == 1); 1269 break; 1270 1271 case DUMP_NAVTREE: 1272 nativeDumpNavTree(); 1273 break; 1274 1275 case SET_JS_FLAGS: 1276 nativeSetJsFlags((String)msg.obj); 1277 break; 1278 1279 case GEOLOCATION_PERMISSIONS_PROVIDE: 1280 GeolocationPermissionsData data = 1281 (GeolocationPermissionsData) msg.obj; 1282 nativeGeolocationPermissionsProvide(data.mOrigin, 1283 data.mAllow, data.mRemember); 1284 break; 1285 1286 case SYNC_SCROLL: 1287 mWebkitScrollX = msg.arg1; 1288 mWebkitScrollY = msg.arg2; 1289 break; 1290 1291 case SPLIT_PICTURE_SET: 1292 nativeSplitContent(); 1293 mSplitPictureIsScheduled = false; 1294 break; 1295 1296 case CLEAR_CONTENT: 1297 // Clear the view so that onDraw() will draw nothing 1298 // but white background 1299 // (See public method WebView.clearView) 1300 nativeClearContent(); 1301 break; 1302 1303 case MESSAGE_RELAY: 1304 if (msg.obj instanceof Message) { 1305 ((Message) msg.obj).sendToTarget(); 1306 } 1307 break; 1308 1309 case POPULATE_VISITED_LINKS: 1310 nativeProvideVisitedHistory((String[])msg.obj); 1311 break; 1312 1313 case VALID_NODE_BOUNDS: { 1314 MotionUpData motionUpData = (MotionUpData) msg.obj; 1315 boolean result = nativeValidNodeAndBounds( 1316 motionUpData.mFrame, motionUpData.mNode, 1317 motionUpData.mBounds); 1318 Message message = mWebView.mPrivateHandler 1319 .obtainMessage(WebView.DO_MOTION_UP, 1320 motionUpData.mX, motionUpData.mY, 1321 new Boolean(result)); 1322 mWebView.mPrivateHandler.sendMessageAtFrontOfQueue( 1323 message); 1324 break; 1325 } 1326 1327 case HIDE_FULLSCREEN: 1328 nativeFullScreenPluginHidden(msg.arg1); 1329 break; 1330 } 1331 } 1332 }; 1333 // Take all queued messages and resend them to the new handler. 1334 synchronized (this) { 1335 int size = mMessages.size(); 1336 for (int i = 0; i < size; i++) { 1337 mHandler.sendMessage(mMessages.get(i)); 1338 } 1339 mMessages = null; 1340 } 1341 } 1342 1343 /** 1344 * Send a message internally to the queue or to the handler 1345 */ 1346 private synchronized void sendMessage(Message msg) { 1347 if (mBlockMessages) { 1348 return; 1349 } 1350 if (mMessages != null) { 1351 mMessages.add(msg); 1352 } else { 1353 mHandler.sendMessage(msg); 1354 } 1355 } 1356 1357 private synchronized void removeMessages(int what) { 1358 if (mBlockMessages) { 1359 return; 1360 } 1361 if (what == EventHub.WEBKIT_DRAW) { 1362 mDrawIsScheduled = false; 1363 } 1364 if (mMessages != null) { 1365 Log.w(LOGTAG, "Not supported in this case."); 1366 } else { 1367 mHandler.removeMessages(what); 1368 } 1369 } 1370 1371 private synchronized boolean hasMessages(int what) { 1372 if (mBlockMessages) { 1373 return false; 1374 } 1375 if (mMessages != null) { 1376 Log.w(LOGTAG, "hasMessages() is not supported in this case."); 1377 return false; 1378 } else { 1379 return mHandler.hasMessages(what); 1380 } 1381 } 1382 1383 private synchronized void sendMessageDelayed(Message msg, long delay) { 1384 if (mBlockMessages) { 1385 return; 1386 } 1387 mHandler.sendMessageDelayed(msg, delay); 1388 } 1389 1390 /** 1391 * Send a message internally to the front of the queue. 1392 */ 1393 private synchronized void sendMessageAtFrontOfQueue(Message msg) { 1394 if (mBlockMessages) { 1395 return; 1396 } 1397 if (mMessages != null) { 1398 mMessages.add(0, msg); 1399 } else { 1400 mHandler.sendMessageAtFrontOfQueue(msg); 1401 } 1402 } 1403 1404 /** 1405 * Remove all the messages. 1406 */ 1407 private synchronized void removeMessages() { 1408 // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed 1409 mDrawIsScheduled = false; 1410 mSplitPictureIsScheduled = false; 1411 if (mMessages != null) { 1412 mMessages.clear(); 1413 } else { 1414 mHandler.removeCallbacksAndMessages(null); 1415 } 1416 } 1417 1418 /** 1419 * Block sending messages to the EventHub. 1420 */ 1421 private synchronized void blockMessages() { 1422 mBlockMessages = true; 1423 } 1424 } 1425 1426 //------------------------------------------------------------------------- 1427 // Methods called by host activity (in the same thread) 1428 //------------------------------------------------------------------------- 1429 1430 void stopLoading() { 1431 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading"); 1432 if (mBrowserFrame != null) { 1433 mBrowserFrame.stopLoading(); 1434 } 1435 } 1436 1437 //------------------------------------------------------------------------- 1438 // Methods called by WebView 1439 // If it refers to local variable, it needs synchronized(). 1440 // If it needs WebCore, it has to send message. 1441 //------------------------------------------------------------------------- 1442 1443 void sendMessage(Message msg) { 1444 mEventHub.sendMessage(msg); 1445 } 1446 1447 void sendMessage(int what) { 1448 mEventHub.sendMessage(Message.obtain(null, what)); 1449 } 1450 1451 void sendMessage(int what, Object obj) { 1452 mEventHub.sendMessage(Message.obtain(null, what, obj)); 1453 } 1454 1455 void sendMessage(int what, int arg1) { 1456 // just ignore the second argument (make it 0) 1457 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0)); 1458 } 1459 1460 void sendMessage(int what, int arg1, int arg2) { 1461 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2)); 1462 } 1463 1464 void sendMessage(int what, int arg1, Object obj) { 1465 // just ignore the second argument (make it 0) 1466 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj)); 1467 } 1468 1469 void sendMessage(int what, int arg1, int arg2, Object obj) { 1470 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj)); 1471 } 1472 1473 void sendMessageAtFrontOfQueue(int what, Object obj) { 1474 mEventHub.sendMessageAtFrontOfQueue(Message.obtain( 1475 null, what, obj)); 1476 } 1477 1478 void sendMessageDelayed(int what, Object obj, long delay) { 1479 mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay); 1480 } 1481 1482 void removeMessages(int what) { 1483 mEventHub.removeMessages(what); 1484 } 1485 1486 void removeMessages() { 1487 mEventHub.removeMessages(); 1488 } 1489 1490 /** 1491 * Removes pending messages and trigger a DESTROY message to send to 1492 * WebCore. 1493 * Called from UI thread. 1494 */ 1495 void destroy() { 1496 // We don't want anyone to post a message between removing pending 1497 // messages and sending the destroy message. 1498 synchronized (mEventHub) { 1499 // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to 1500 // be preserved even the WebView is destroyed. 1501 // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS 1502 boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS); 1503 boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS); 1504 mEventHub.removeMessages(); 1505 mEventHub.sendMessageAtFrontOfQueue( 1506 Message.obtain(null, EventHub.DESTROY)); 1507 if (hasPause) { 1508 mEventHub.sendMessageAtFrontOfQueue( 1509 Message.obtain(null, EventHub.PAUSE_TIMERS)); 1510 } 1511 if (hasResume) { 1512 mEventHub.sendMessageAtFrontOfQueue( 1513 Message.obtain(null, EventHub.RESUME_TIMERS)); 1514 } 1515 mEventHub.blockMessages(); 1516 } 1517 } 1518 1519 //------------------------------------------------------------------------- 1520 // WebViewCore private methods 1521 //------------------------------------------------------------------------- 1522 1523 private void clearCache(boolean includeDiskFiles) { 1524 mBrowserFrame.clearCache(); 1525 if (includeDiskFiles) { 1526 CacheManager.removeAllCacheFiles(); 1527 } 1528 } 1529 1530 private void loadUrl(String url) { 1531 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url); 1532 mBrowserFrame.loadUrl(url); 1533 } 1534 1535 private void key(KeyEvent evt, boolean isDown) { 1536 if (DebugFlags.WEB_VIEW_CORE) { 1537 Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", " 1538 + evt); 1539 } 1540 int keyCode = evt.getKeyCode(); 1541 if (!nativeKey(keyCode, evt.getUnicodeChar(), 1542 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(), 1543 evt.isSymPressed(), 1544 isDown) && keyCode != KeyEvent.KEYCODE_ENTER) { 1545 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP 1546 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { 1547 if (DebugFlags.WEB_VIEW_CORE) { 1548 Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode); 1549 } 1550 if (mWebView != null && evt.isDown()) { 1551 Message.obtain(mWebView.mPrivateHandler, 1552 WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget(); 1553 } 1554 return; 1555 } 1556 // bubble up the event handling 1557 // but do not bubble up the ENTER key, which would open the search 1558 // bar without any text. 1559 mCallbackProxy.onUnhandledKeyEvent(evt); 1560 } 1561 } 1562 1563 // These values are used to avoid requesting a layout based on old values 1564 private int mCurrentViewWidth = 0; 1565 private int mCurrentViewHeight = 0; 1566 private float mCurrentViewScale = 1.0f; 1567 1568 // notify webkit that our virtual view size changed size (after inv-zoom) 1569 private void viewSizeChanged(int w, int h, int textwrapWidth, float scale, 1570 boolean ignoreHeight) { 1571 if (DebugFlags.WEB_VIEW_CORE) { 1572 Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h 1573 + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale); 1574 } 1575 if (w == 0) { 1576 Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); 1577 return; 1578 } 1579 int width = w; 1580 if (mSettings.getUseWideViewPort()) { 1581 if (mViewportWidth == -1) { 1582 if (mSettings.getLayoutAlgorithm() == 1583 WebSettings.LayoutAlgorithm.NORMAL) { 1584 width = WebView.DEFAULT_VIEWPORT_WIDTH; 1585 } else { 1586 /* 1587 * if a page's minimum preferred width is wider than the 1588 * given "w", use it instead to get better layout result. If 1589 * we start a page with MAX_ZOOM_WIDTH, "w" will be always 1590 * wider. If we start a page with screen width, due to the 1591 * delay between {@link #didFirstLayout} and 1592 * {@link #viewSizeChanged}, 1593 * {@link #nativeGetContentMinPrefWidth} will return a more 1594 * accurate value than initial 0 to result a better layout. 1595 * In the worse case, the native width will be adjusted when 1596 * next zoom or screen orientation change happens. 1597 */ 1598 width = Math.min(WebView.sMaxViewportWidth, Math.max(w, 1599 Math.max(WebView.DEFAULT_VIEWPORT_WIDTH, 1600 nativeGetContentMinPrefWidth()))); 1601 } 1602 } else { 1603 width = Math.max(w, mViewportWidth); 1604 } 1605 } 1606 nativeSetSize(width, width == w ? h : Math.round((float) width * h / w), 1607 textwrapWidth, scale, w, h, ignoreHeight); 1608 // Remember the current width and height 1609 boolean needInvalidate = (mCurrentViewWidth == 0); 1610 mCurrentViewWidth = w; 1611 mCurrentViewHeight = h; 1612 mCurrentViewScale = scale; 1613 if (needInvalidate) { 1614 // ensure {@link #webkitDraw} is called as we were blocking in 1615 // {@link #contentDraw} when mCurrentViewWidth is 0 1616 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged"); 1617 contentDraw(); 1618 } 1619 mEventHub.sendMessage(Message.obtain(null, 1620 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY)); 1621 } 1622 1623 private void sendUpdateTextEntry() { 1624 if (mWebView != null) { 1625 Message.obtain(mWebView.mPrivateHandler, 1626 WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget(); 1627 } 1628 } 1629 1630 // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize 1631 // callbacks. Computes the sum of database quota for all origins. 1632 private long getUsedQuota() { 1633 WebStorage webStorage = WebStorage.getInstance(); 1634 Collection<WebStorage.Origin> origins = webStorage.getOriginsSync(); 1635 1636 if (origins == null) { 1637 return 0; 1638 } 1639 long usedQuota = 0; 1640 for (WebStorage.Origin website : origins) { 1641 usedQuota += website.getQuota(); 1642 } 1643 return usedQuota; 1644 } 1645 1646 // Used to avoid posting more than one draw message. 1647 private boolean mDrawIsScheduled; 1648 1649 // Used to avoid posting more than one split picture message. 1650 private boolean mSplitPictureIsScheduled; 1651 1652 // mRestoreState is set in didFirstLayout(), and reset in the next 1653 // webkitDraw after passing it to the UI thread. 1654 private RestoreState mRestoreState = null; 1655 1656 static class RestoreState { 1657 float mMinScale; 1658 float mMaxScale; 1659 float mViewScale; 1660 float mTextWrapScale; 1661 float mDefaultScale; 1662 int mScrollX; 1663 int mScrollY; 1664 boolean mMobileSite; 1665 } 1666 1667 static class DrawData { 1668 DrawData() { 1669 mInvalRegion = new Region(); 1670 mWidthHeight = new Point(); 1671 } 1672 Region mInvalRegion; 1673 Point mViewPoint; 1674 Point mWidthHeight; 1675 int mMinPrefWidth; 1676 RestoreState mRestoreState; // only non-null if it is for the first 1677 // picture set after the first layout 1678 boolean mFocusSizeChanged; 1679 } 1680 1681 private void webkitDraw() { 1682 mDrawIsScheduled = false; 1683 DrawData draw = new DrawData(); 1684 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); 1685 if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight) 1686 == false) { 1687 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort"); 1688 return; 1689 } 1690 if (mWebView != null) { 1691 // Send the native view size that was used during the most recent 1692 // layout. 1693 draw.mFocusSizeChanged = nativeFocusBoundsChanged(); 1694 draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight); 1695 if (mSettings.getUseWideViewPort()) { 1696 draw.mMinPrefWidth = Math.max( 1697 mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH 1698 : (mViewportWidth == 0 ? mCurrentViewWidth 1699 : mViewportWidth), 1700 nativeGetContentMinPrefWidth()); 1701 } 1702 if (mRestoreState != null) { 1703 draw.mRestoreState = mRestoreState; 1704 mRestoreState = null; 1705 } 1706 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); 1707 Message.obtain(mWebView.mPrivateHandler, 1708 WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); 1709 if (mWebkitScrollX != 0 || mWebkitScrollY != 0) { 1710 // as we have the new picture, try to sync the scroll position 1711 Message.obtain(mWebView.mPrivateHandler, 1712 WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX, 1713 mWebkitScrollY).sendToTarget(); 1714 mWebkitScrollX = mWebkitScrollY = 0; 1715 } 1716 } 1717 } 1718 1719 /////////////////////////////////////////////////////////////////////////// 1720 // These are called from the UI thread, not our thread 1721 1722 static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG | 1723 Paint.DITHER_FLAG | 1724 Paint.SUBPIXEL_TEXT_FLAG; 1725 static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG | 1726 Paint.DITHER_FLAG; 1727 1728 final DrawFilter mZoomFilter = 1729 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG); 1730 final DrawFilter mScrollFilter = null; 1731 // If we need to trade more speed for less quality on slower devices 1732 // use this: new PaintFlagsDrawFilter(SCROLL_BITS, 0); 1733 1734 /* package */ void drawContentPicture(Canvas canvas, int color, 1735 boolean animatingZoom, 1736 boolean animatingScroll) { 1737 DrawFilter df = null; 1738 if (animatingZoom) { 1739 df = mZoomFilter; 1740 } else if (animatingScroll) { 1741 df = mScrollFilter; 1742 } 1743 canvas.setDrawFilter(df); 1744 boolean tookTooLong = nativeDrawContent(canvas, color); 1745 canvas.setDrawFilter(null); 1746 if (tookTooLong && mSplitPictureIsScheduled == false) { 1747 mSplitPictureIsScheduled = true; 1748 sendMessage(EventHub.SPLIT_PICTURE_SET); 1749 } 1750 } 1751 1752 /* package */ synchronized boolean pictureReady() { 1753 return 0 != mNativeClass ? nativePictureReady() : false; 1754 } 1755 1756 /*package*/ synchronized Picture copyContentPicture() { 1757 Picture result = new Picture(); 1758 if (0 != mNativeClass) { 1759 nativeCopyContentToPicture(result); 1760 } 1761 return result; 1762 } 1763 1764 static void pauseUpdate(WebViewCore core) { 1765 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1766 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1767 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1768 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1769 .obtainMessage(WebCoreThread.REDUCE_PRIORITY)); 1770 } 1771 1772 static void resumeUpdate(WebViewCore core) { 1773 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1774 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1775 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1776 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1777 .obtainMessage(WebCoreThread.RESUME_PRIORITY)); 1778 } 1779 1780 static void startCacheTransaction() { 1781 sWebCoreHandler.sendMessage(sWebCoreHandler 1782 .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER)); 1783 } 1784 1785 static void endCacheTransaction() { 1786 sWebCoreHandler.sendMessage(sWebCoreHandler 1787 .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER)); 1788 } 1789 1790 ////////////////////////////////////////////////////////////////////////// 1791 1792 private void restoreState(int index) { 1793 WebBackForwardList list = mCallbackProxy.getBackForwardList(); 1794 int size = list.getSize(); 1795 for (int i = 0; i < size; i++) { 1796 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); 1797 } 1798 mBrowserFrame.mLoadInitFromJava = true; 1799 list.restoreIndex(mBrowserFrame.mNativeFrame, index); 1800 mBrowserFrame.mLoadInitFromJava = false; 1801 } 1802 1803 //------------------------------------------------------------------------- 1804 // Implement abstract methods in WebViewCore, native WebKit callback part 1805 //------------------------------------------------------------------------- 1806 1807 // called from JNI or WebView thread 1808 /* package */ void contentDraw() { 1809 // don't update the Picture until we have an initial width and finish 1810 // the first layout 1811 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { 1812 return; 1813 } 1814 // only fire an event if this is our first request 1815 synchronized (this) { 1816 if (mDrawIsScheduled) return; 1817 mDrawIsScheduled = true; 1818 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 1819 } 1820 } 1821 1822 // called by JNI 1823 private void contentScrollBy(int dx, int dy, boolean animate) { 1824 if (!mBrowserFrame.firstLayoutDone()) { 1825 // Will this happen? If yes, we need to do something here. 1826 return; 1827 } 1828 if (mWebView != null) { 1829 Message msg = Message.obtain(mWebView.mPrivateHandler, 1830 WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate)); 1831 if (mDrawIsScheduled) { 1832 mEventHub.sendMessage(Message.obtain(null, 1833 EventHub.MESSAGE_RELAY, msg)); 1834 } else { 1835 msg.sendToTarget(); 1836 } 1837 } 1838 } 1839 1840 // called by JNI 1841 private void contentScrollTo(int x, int y) { 1842 if (!mBrowserFrame.firstLayoutDone()) { 1843 /* 1844 * WebKit restore state will be called before didFirstLayout(), 1845 * remember the position as it has to be applied after restoring 1846 * zoom factor which is controlled by screenWidth. 1847 */ 1848 mRestoredX = x; 1849 mRestoredY = y; 1850 return; 1851 } 1852 if (mWebView != null) { 1853 Message msg = Message.obtain(mWebView.mPrivateHandler, 1854 WebView.SCROLL_TO_MSG_ID, x, y); 1855 if (mDrawIsScheduled) { 1856 mEventHub.sendMessage(Message.obtain(null, 1857 EventHub.MESSAGE_RELAY, msg)); 1858 } else { 1859 msg.sendToTarget(); 1860 } 1861 } 1862 } 1863 1864 // called by JNI 1865 private void contentSpawnScrollTo(int x, int y) { 1866 if (!mBrowserFrame.firstLayoutDone()) { 1867 /* 1868 * WebKit restore state will be called before didFirstLayout(), 1869 * remember the position as it has to be applied after restoring 1870 * zoom factor which is controlled by screenWidth. 1871 */ 1872 mRestoredX = x; 1873 mRestoredY = y; 1874 return; 1875 } 1876 if (mWebView != null) { 1877 Message msg = Message.obtain(mWebView.mPrivateHandler, 1878 WebView.SPAWN_SCROLL_TO_MSG_ID, x, y); 1879 if (mDrawIsScheduled) { 1880 mEventHub.sendMessage(Message.obtain(null, 1881 EventHub.MESSAGE_RELAY, msg)); 1882 } else { 1883 msg.sendToTarget(); 1884 } 1885 } 1886 } 1887 1888 // called by JNI 1889 private void sendNotifyProgressFinished() { 1890 sendUpdateTextEntry(); 1891 // as CacheManager can behave based on database transaction, we need to 1892 // call tick() to trigger endTransaction 1893 sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER); 1894 sWebCoreHandler.sendMessage(sWebCoreHandler 1895 .obtainMessage(WebCoreThread.CACHE_TICKER)); 1896 contentDraw(); 1897 } 1898 1899 /* Called by JNI. The coordinates are in doc coordinates, so they need to 1900 be scaled before they can be used by the view system, which happens 1901 in WebView since it (and its thread) know the current scale factor. 1902 */ 1903 private void sendViewInvalidate(int left, int top, int right, int bottom) { 1904 if (mWebView != null) { 1905 Message.obtain(mWebView.mPrivateHandler, 1906 WebView.INVAL_RECT_MSG_ID, 1907 new Rect(left, top, right, bottom)).sendToTarget(); 1908 } 1909 } 1910 1911 /* package */ WebView getWebView() { 1912 return mWebView; 1913 } 1914 1915 private native void setViewportSettingsFromNative(); 1916 1917 // called by JNI 1918 private void didFirstLayout(boolean standardLoad) { 1919 if (DebugFlags.WEB_VIEW_CORE) { 1920 Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad); 1921 } 1922 1923 mBrowserFrame.didFirstLayout(); 1924 1925 if (mWebView == null) return; 1926 1927 boolean updateRestoreState = standardLoad || mRestoredScale > 0; 1928 setupViewport(updateRestoreState); 1929 // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will 1930 // be called after the WebView restore the state. If updateRestoreState 1931 // is false, start to draw now as it is ready. 1932 if (!updateRestoreState) { 1933 mWebView.mViewManager.postReadyToDrawAll(); 1934 } 1935 1936 // reset the scroll position, the restored offset and scales 1937 mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY 1938 = mRestoredScale = mRestoredScreenWidthScale = 0; 1939 } 1940 1941 // called by JNI 1942 private void updateViewport() { 1943 // if updateViewport is called before first layout, wait until first 1944 // layout to update the viewport. In the rare case, this is called after 1945 // first layout, force an update as we have just parsed the viewport 1946 // meta tag. 1947 if (mBrowserFrame.firstLayoutDone()) { 1948 setupViewport(true); 1949 } 1950 } 1951 1952 private void setupViewport(boolean updateRestoreState) { 1953 // set the viewport settings from WebKit 1954 setViewportSettingsFromNative(); 1955 1956 // adjust the default scale to match the densityDpi 1957 float adjust = 1.0f; 1958 if (mViewportDensityDpi == -1) { 1959 if (WebView.DEFAULT_SCALE_PERCENT != 100) { 1960 adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f; 1961 } 1962 } else if (mViewportDensityDpi > 0) { 1963 adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi 1964 / mViewportDensityDpi; 1965 } 1966 int defaultScale = (int) (adjust * 100); 1967 1968 if (mViewportInitialScale > 0) { 1969 mViewportInitialScale *= adjust; 1970 } 1971 if (mViewportMinimumScale > 0) { 1972 mViewportMinimumScale *= adjust; 1973 } 1974 if (mViewportMaximumScale > 0) { 1975 mViewportMaximumScale *= adjust; 1976 } 1977 1978 // infer the values if they are not defined. 1979 if (mViewportWidth == 0) { 1980 if (mViewportInitialScale == 0) { 1981 mViewportInitialScale = defaultScale; 1982 } 1983 } 1984 if (mViewportUserScalable == false) { 1985 mViewportInitialScale = defaultScale; 1986 mViewportMinimumScale = defaultScale; 1987 mViewportMaximumScale = defaultScale; 1988 } 1989 if (mViewportMinimumScale > mViewportInitialScale 1990 && mViewportInitialScale != 0) { 1991 mViewportMinimumScale = mViewportInitialScale; 1992 } 1993 if (mViewportMaximumScale > 0 1994 && mViewportMaximumScale < mViewportInitialScale) { 1995 mViewportMaximumScale = mViewportInitialScale; 1996 } 1997 if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) { 1998 mViewportWidth = 0; 1999 } 2000 2001 // if mViewportWidth is 0, it means device-width, always update. 2002 if (mViewportWidth != 0 && !updateRestoreState) return; 2003 2004 // now notify webview 2005 // webViewWidth refers to the width in the view system 2006 int webViewWidth; 2007 // viewportWidth refers to the width in the document system 2008 int viewportWidth = mCurrentViewWidth; 2009 if (viewportWidth == 0) { 2010 // this may happen when WebView just starts. This is not perfect as 2011 // we call WebView method from WebCore thread. But not perfect 2012 // reference is better than no reference. 2013 webViewWidth = mWebView.getViewWidth(); 2014 viewportWidth = (int) (webViewWidth / adjust); 2015 if (viewportWidth == 0) { 2016 Log.w(LOGTAG, "Can't get the viewWidth after the first layout"); 2017 } 2018 } else { 2019 webViewWidth = Math.round(viewportWidth * mCurrentViewScale); 2020 } 2021 mRestoreState = new RestoreState(); 2022 mRestoreState.mMinScale = mViewportMinimumScale / 100.0f; 2023 mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f; 2024 mRestoreState.mDefaultScale = adjust; 2025 mRestoreState.mScrollX = mRestoredX; 2026 mRestoreState.mScrollY = mRestoredY; 2027 mRestoreState.mMobileSite = (0 == mViewportWidth); 2028 if (mRestoredScale > 0) { 2029 if (mRestoredScreenWidthScale > 0) { 2030 mRestoreState.mTextWrapScale = 2031 mRestoredScreenWidthScale / 100.0f; 2032 // 0 will trigger WebView to turn on zoom overview mode 2033 mRestoreState.mViewScale = 0; 2034 } else { 2035 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 2036 mRestoredScale / 100.0f; 2037 } 2038 } else { 2039 if (mViewportInitialScale > 0) { 2040 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 2041 mViewportInitialScale / 100.0f; 2042 } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) { 2043 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 2044 (float) webViewWidth / mViewportWidth; 2045 } else { 2046 mRestoreState.mTextWrapScale = adjust; 2047 // 0 will trigger WebView to turn on zoom overview mode 2048 mRestoreState.mViewScale = 0; 2049 } 2050 } 2051 2052 if (mWebView.mHeightCanMeasure) { 2053 // Trick to ensure that the Picture has the exact height for the 2054 // content by forcing to layout with 0 height after the page is 2055 // ready, which is indicated by didFirstLayout. This is essential to 2056 // get rid of the white space in the GMail which uses WebView for 2057 // message view. 2058 mWebView.mLastHeightSent = 0; 2059 // Send a negative scale to indicate that WebCore should reuse 2060 // the current scale 2061 WebView.ViewSizeData data = new WebView.ViewSizeData(); 2062 data.mWidth = mWebView.mLastWidthSent; 2063 data.mHeight = 0; 2064 // if mHeightCanMeasure is true, getUseWideViewPort() can't be 2065 // true. It is safe to use mWidth for mTextWrapWidth. 2066 data.mTextWrapWidth = data.mWidth; 2067 data.mScale = -1.0f; 2068 data.mIgnoreHeight = false; 2069 // send VIEW_SIZE_CHANGED to the front of the queue so that we can 2070 // avoid pushing the wrong picture to the WebView side. If there is 2071 // a VIEW_SIZE_CHANGED in the queue, probably from WebView side, 2072 // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED 2073 // in the queue, as mLastHeightSent has been updated here, we may 2074 // miss the requestLayout in WebView side after the new picture. 2075 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2076 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 2077 EventHub.VIEW_SIZE_CHANGED, data)); 2078 } else if (mSettings.getUseWideViewPort()) { 2079 if (viewportWidth == 0) { 2080 // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView 2081 // to WebViewCore 2082 mWebView.mLastWidthSent = 0; 2083 } else { 2084 WebView.ViewSizeData data = new WebView.ViewSizeData(); 2085 // mViewScale as 0 means it is in zoom overview mode. So we don't 2086 // know the exact scale. If mRestoredScale is non-zero, use it; 2087 // otherwise just use mTextWrapScale as the initial scale. 2088 data.mScale = mRestoreState.mViewScale == 0 2089 ? (mRestoredScale > 0 ? mRestoredScale / 100.0f 2090 : mRestoreState.mTextWrapScale) 2091 : mRestoreState.mViewScale; 2092 if (DebugFlags.WEB_VIEW_CORE) { 2093 Log.v(LOGTAG, "setupViewport" 2094 + " mRestoredScale=" + mRestoredScale 2095 + " mViewScale=" + mRestoreState.mViewScale 2096 + " mTextWrapScale=" + mRestoreState.mTextWrapScale 2097 ); 2098 } 2099 data.mWidth = Math.round(webViewWidth / data.mScale); 2100 data.mHeight = mCurrentViewHeight * data.mWidth / viewportWidth; 2101 data.mTextWrapWidth = Math.round(webViewWidth 2102 / mRestoreState.mTextWrapScale); 2103 data.mIgnoreHeight = false; 2104 // send VIEW_SIZE_CHANGED to the front of the queue so that we 2105 // can avoid pushing the wrong picture to the WebView side. 2106 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2107 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 2108 EventHub.VIEW_SIZE_CHANGED, data)); 2109 } 2110 } 2111 } 2112 2113 // called by JNI 2114 private void restoreScale(int scale) { 2115 if (mBrowserFrame.firstLayoutDone() == false) { 2116 mRestoredScale = scale; 2117 } 2118 } 2119 2120 // called by JNI 2121 private void restoreScreenWidthScale(int scale) { 2122 if (!mSettings.getUseWideViewPort()) { 2123 return; 2124 } 2125 2126 if (mBrowserFrame.firstLayoutDone() == false) { 2127 mRestoredScreenWidthScale = scale; 2128 } 2129 } 2130 2131 // called by JNI 2132 private void needTouchEvents(boolean need) { 2133 if (mWebView != null) { 2134 Message.obtain(mWebView.mPrivateHandler, 2135 WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0) 2136 .sendToTarget(); 2137 } 2138 } 2139 2140 // called by JNI 2141 private void updateTextfield(int ptr, boolean changeToPassword, 2142 String text, int textGeneration) { 2143 if (mWebView != null) { 2144 Message msg = Message.obtain(mWebView.mPrivateHandler, 2145 WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 2146 textGeneration, text); 2147 msg.getData().putBoolean("password", changeToPassword); 2148 msg.sendToTarget(); 2149 } 2150 } 2151 2152 // called by JNI 2153 private void updateTextSelection(int pointer, int start, int end, 2154 int textGeneration) { 2155 if (mWebView != null) { 2156 Message.obtain(mWebView.mPrivateHandler, 2157 WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration, 2158 new TextSelectionData(start, end)).sendToTarget(); 2159 } 2160 } 2161 2162 // called by JNI 2163 private void clearTextEntry() { 2164 if (mWebView == null) return; 2165 Message.obtain(mWebView.mPrivateHandler, 2166 WebView.CLEAR_TEXT_ENTRY).sendToTarget(); 2167 } 2168 2169 // called by JNI 2170 private void sendFindAgain() { 2171 if (mWebView == null) return; 2172 Message.obtain(mWebView.mPrivateHandler, 2173 WebView.FIND_AGAIN).sendToTarget(); 2174 } 2175 2176 private native void nativeUpdateFrameCacheIfLoading(); 2177 2178 /** 2179 * Scroll the focused textfield to (xPercent, y) in document space 2180 */ 2181 private native void nativeScrollFocusedTextInput(float xPercent, int y); 2182 2183 // these must be in document space (i.e. not scaled/zoomed). 2184 private native void nativeSetScrollOffset(int gen, int dx, int dy); 2185 2186 private native void nativeSetGlobalBounds(int x, int y, int w, int h); 2187 2188 // called by JNI 2189 private void requestListBox(String[] array, int[] enabledArray, 2190 int[] selectedArray) { 2191 if (mWebView != null) { 2192 mWebView.requestListBox(array, enabledArray, selectedArray); 2193 } 2194 } 2195 2196 // called by JNI 2197 private void requestListBox(String[] array, int[] enabledArray, 2198 int selection) { 2199 if (mWebView != null) { 2200 mWebView.requestListBox(array, enabledArray, selection); 2201 } 2202 2203 } 2204 2205 // called by JNI 2206 private void requestKeyboard(boolean showKeyboard) { 2207 if (mWebView != null) { 2208 Message.obtain(mWebView.mPrivateHandler, 2209 WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0) 2210 .sendToTarget(); 2211 } 2212 } 2213 2214 // called by JNI 2215 private Class<?> getPluginClass(String libName, String clsName) { 2216 2217 if (mWebView == null) { 2218 return null; 2219 } 2220 2221 PluginManager pluginManager = PluginManager.getInstance(null); 2222 2223 String pkgName = pluginManager.getPluginsAPKName(libName); 2224 if (pkgName == null) { 2225 Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); 2226 return null; 2227 } 2228 2229 try { 2230 return pluginManager.getPluginClass(pkgName, clsName); 2231 } catch (NameNotFoundException e) { 2232 Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")"); 2233 } catch (ClassNotFoundException e) { 2234 Log.e(LOGTAG, "Unable to find plugin class (" + clsName + 2235 ") in the apk (" + pkgName + ")"); 2236 } 2237 2238 return null; 2239 } 2240 2241 private WebkitPlugin createPluginJavaInstance(String libName, int npp) { 2242 2243 if (mWebView == null) { 2244 return null; 2245 } 2246 2247 PluginManager pluginManager = PluginManager.getInstance(null); 2248 2249 String pkgName = pluginManager.getPluginsAPKName(libName); 2250 if (pkgName == null) { 2251 Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); 2252 return null; 2253 } 2254 2255 return pluginManager.getPluginInstance(pkgName, npp); 2256 } 2257 2258 // called by JNI. PluginWidget function to launch a full-screen view using a 2259 // View object provided by the plugin class. 2260 private void showFullScreenPlugin(WebkitPlugin webkitPlugin, final int npp, 2261 int x, int y, int width, int height) { 2262 if (mWebView == null) { 2263 return; 2264 } 2265 2266 final SurfaceDrawingModel surface = webkitPlugin.getFullScreenSurface(); 2267 if(surface == null) { 2268 Log.e(LOGTAG, "Attempted to create an full-screen surface with a " + 2269 "null drawing model"); 2270 return; 2271 } 2272 2273 PluginFullScreenData data = new PluginFullScreenData(); 2274 data.mView = surface.getSurface(); 2275 data.mNpp = npp; 2276 data.mDocX = x; 2277 data.mDocY = y; 2278 data.mDocWidth = width; 2279 data.mDocHeight = height; 2280 mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN, data) 2281 .sendToTarget(); 2282 } 2283 2284 // called by JNI 2285 private void hideFullScreenPlugin() { 2286 if (mWebView == null) { 2287 return; 2288 } 2289 mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN) 2290 .sendToTarget(); 2291 } 2292 2293 // called by JNI 2294 private void updateFullScreenPlugin(int x, int y, int width, int height) { 2295 if (mWebView == null) { 2296 return; 2297 } 2298 2299 PluginFullScreenData data = new PluginFullScreenData(); 2300 data.mDocX = x; 2301 data.mDocY = y; 2302 data.mDocWidth = width; 2303 data.mDocHeight = height; 2304 // null mView and mNpp to indicate it is an update 2305 mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN, data) 2306 .sendToTarget(); 2307 } 2308 2309 // called by JNI. PluginWidget functions for creating an embedded View for 2310 // the surface drawing model. 2311 private ViewManager.ChildView createSurface(WebkitPlugin webkitPlugin, 2312 int x, int y, int width, int height) { 2313 2314 if (mWebView == null) { 2315 return null; 2316 } 2317 2318 SurfaceDrawingModel embeddedSurface = webkitPlugin.getEmbeddedSurface(); 2319 if(embeddedSurface == null) { 2320 Log.e(LOGTAG, "Attempted to create an embedded surface with a null drawing model"); 2321 return null; 2322 } 2323 2324 View pluginView = embeddedSurface.getSurface(); 2325 pluginView.setWillNotDraw(false); 2326 2327 ViewManager.ChildView view = mWebView.mViewManager.createView(); 2328 view.mView = pluginView; 2329 view.attachView(x, y, width, height); 2330 return view; 2331 } 2332 2333 private void updateSurface(ViewManager.ChildView childView, int x, int y, 2334 int width, int height) { 2335 childView.attachView(x, y, width, height); 2336 } 2337 2338 private void destroySurface(ViewManager.ChildView childView) { 2339 childView.removeView(); 2340 } 2341 2342 private native void nativePause(); 2343 private native void nativeResume(); 2344 private native void nativeFreeMemory(); 2345 private native void nativeFullScreenPluginHidden(int npp); 2346 private native boolean nativeValidNodeAndBounds(int frame, int node, 2347 Rect bounds); 2348 2349} 2350