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