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