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