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