WebViewCore.java revision 188bf8deb4468d3901c466352bf88378819d04d6
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 plugin: " + keyCode); 1577 } 1578 if (mWebView != null && evt.isDown()) { 1579 Message.obtain(mWebView.mPrivateHandler, 1580 WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget(); 1581 } 1582 return; 1583 } 1584 // bubble up the event handling 1585 // but do not bubble up the ENTER key, which would open the search 1586 // bar without any text. 1587 mCallbackProxy.onUnhandledKeyEvent(evt); 1588 } 1589 } 1590 1591 // These values are used to avoid requesting a layout based on old values 1592 private int mCurrentViewWidth = 0; 1593 private int mCurrentViewHeight = 0; 1594 private float mCurrentViewScale = 1.0f; 1595 1596 // notify webkit that our virtual view size changed size (after inv-zoom) 1597 private void viewSizeChanged(int w, int h, int textwrapWidth, float scale, 1598 int anchorX, int anchorY, boolean ignoreHeight) { 1599 if (DebugFlags.WEB_VIEW_CORE) { 1600 Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h 1601 + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale); 1602 } 1603 if (w == 0) { 1604 Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); 1605 return; 1606 } 1607 int width = w; 1608 if (mSettings.getUseWideViewPort()) { 1609 if (mViewportWidth == -1) { 1610 if (mSettings.getLayoutAlgorithm() == 1611 WebSettings.LayoutAlgorithm.NORMAL) { 1612 width = WebView.DEFAULT_VIEWPORT_WIDTH; 1613 } else { 1614 /* 1615 * if a page's minimum preferred width is wider than the 1616 * given "w", use it instead to get better layout result. If 1617 * we start a page with MAX_ZOOM_WIDTH, "w" will be always 1618 * wider. If we start a page with screen width, due to the 1619 * delay between {@link #didFirstLayout} and 1620 * {@link #viewSizeChanged}, 1621 * {@link #nativeGetContentMinPrefWidth} will return a more 1622 * accurate value than initial 0 to result a better layout. 1623 * In the worse case, the native width will be adjusted when 1624 * next zoom or screen orientation change happens. 1625 */ 1626 width = Math.min(WebView.sMaxViewportWidth, Math.max(w, 1627 Math.max(WebView.DEFAULT_VIEWPORT_WIDTH, 1628 nativeGetContentMinPrefWidth()))); 1629 } 1630 } else if (mViewportWidth > 0) { 1631 width = Math.max(w, mViewportWidth); 1632 } else { 1633 width = textwrapWidth; 1634 } 1635 } 1636 nativeSetSize(width, width == w ? h : Math.round((float) width * h / w), 1637 textwrapWidth, scale, w, h, anchorX, anchorY, ignoreHeight); 1638 // Remember the current width and height 1639 boolean needInvalidate = (mCurrentViewWidth == 0); 1640 mCurrentViewWidth = w; 1641 mCurrentViewHeight = h; 1642 mCurrentViewScale = scale; 1643 if (needInvalidate) { 1644 // ensure {@link #webkitDraw} is called as we were blocking in 1645 // {@link #contentDraw} when mCurrentViewWidth is 0 1646 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged"); 1647 contentDraw(); 1648 } 1649 mEventHub.sendMessage(Message.obtain(null, 1650 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY)); 1651 } 1652 1653 private void sendUpdateTextEntry() { 1654 if (mWebView != null) { 1655 Message.obtain(mWebView.mPrivateHandler, 1656 WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget(); 1657 } 1658 } 1659 1660 // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize 1661 // callbacks. Computes the sum of database quota for all origins. 1662 private long getUsedQuota() { 1663 WebStorage webStorage = WebStorage.getInstance(); 1664 Collection<WebStorage.Origin> origins = webStorage.getOriginsSync(); 1665 1666 if (origins == null) { 1667 return 0; 1668 } 1669 long usedQuota = 0; 1670 for (WebStorage.Origin website : origins) { 1671 usedQuota += website.getQuota(); 1672 } 1673 return usedQuota; 1674 } 1675 1676 // Used to avoid posting more than one draw message. 1677 private boolean mDrawIsScheduled; 1678 1679 // Used to avoid posting more than one split picture message. 1680 private boolean mSplitPictureIsScheduled; 1681 1682 // Used to suspend drawing. 1683 private boolean mDrawIsPaused; 1684 1685 // mRestoreState is set in didFirstLayout(), and reset in the next 1686 // webkitDraw after passing it to the UI thread. 1687 private RestoreState mRestoreState = null; 1688 1689 static class RestoreState { 1690 float mMinScale; 1691 float mMaxScale; 1692 float mViewScale; 1693 float mTextWrapScale; 1694 float mDefaultScale; 1695 int mScrollX; 1696 int mScrollY; 1697 boolean mMobileSite; 1698 } 1699 1700 static class DrawData { 1701 DrawData() { 1702 mInvalRegion = new Region(); 1703 mWidthHeight = new Point(); 1704 } 1705 Region mInvalRegion; 1706 Point mViewPoint; 1707 Point mWidthHeight; 1708 int mMinPrefWidth; 1709 RestoreState mRestoreState; // only non-null if it is for the first 1710 // picture set after the first layout 1711 boolean mFocusSizeChanged; 1712 } 1713 1714 private void webkitDraw() { 1715 mDrawIsScheduled = false; 1716 DrawData draw = new DrawData(); 1717 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); 1718 if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight) 1719 == false) { 1720 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort"); 1721 return; 1722 } 1723 if (mWebView != null) { 1724 // Send the native view size that was used during the most recent 1725 // layout. 1726 draw.mFocusSizeChanged = nativeFocusBoundsChanged(); 1727 draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight); 1728 if (mSettings.getUseWideViewPort()) { 1729 draw.mMinPrefWidth = Math.max( 1730 mViewportWidth == -1 ? WebView.DEFAULT_VIEWPORT_WIDTH 1731 : (mViewportWidth == 0 ? mCurrentViewWidth 1732 : mViewportWidth), 1733 nativeGetContentMinPrefWidth()); 1734 } 1735 if (mRestoreState != null) { 1736 draw.mRestoreState = mRestoreState; 1737 mRestoreState = null; 1738 } 1739 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); 1740 Message.obtain(mWebView.mPrivateHandler, 1741 WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); 1742 if (mWebkitScrollX != 0 || mWebkitScrollY != 0) { 1743 // as we have the new picture, try to sync the scroll position 1744 Message.obtain(mWebView.mPrivateHandler, 1745 WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX, 1746 mWebkitScrollY).sendToTarget(); 1747 mWebkitScrollX = mWebkitScrollY = 0; 1748 } 1749 } 1750 } 1751 1752 /////////////////////////////////////////////////////////////////////////// 1753 // These are called from the UI thread, not our thread 1754 1755 static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG | 1756 Paint.DITHER_FLAG | 1757 Paint.SUBPIXEL_TEXT_FLAG; 1758 static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG | 1759 Paint.DITHER_FLAG; 1760 1761 final DrawFilter mZoomFilter = 1762 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG); 1763 // If we need to trade better quality for speed, set mScrollFilter to null 1764 final DrawFilter mScrollFilter = 1765 new PaintFlagsDrawFilter(SCROLL_BITS, 0); 1766 1767 /* package */ void drawContentPicture(Canvas canvas, int color, 1768 boolean animatingZoom, 1769 boolean animatingScroll) { 1770 DrawFilter df = null; 1771 if (animatingZoom) { 1772 df = mZoomFilter; 1773 } else if (animatingScroll) { 1774 df = mScrollFilter; 1775 } 1776 canvas.setDrawFilter(df); 1777 boolean tookTooLong = nativeDrawContent(canvas, color); 1778 canvas.setDrawFilter(null); 1779 if (tookTooLong && mSplitPictureIsScheduled == false) { 1780 mSplitPictureIsScheduled = true; 1781 sendMessage(EventHub.SPLIT_PICTURE_SET); 1782 } 1783 } 1784 1785 /* package */ synchronized boolean pictureReady() { 1786 return 0 != mNativeClass ? nativePictureReady() : false; 1787 } 1788 1789 /*package*/ synchronized Picture copyContentPicture() { 1790 Picture result = new Picture(); 1791 if (0 != mNativeClass) { 1792 nativeCopyContentToPicture(result); 1793 } 1794 return result; 1795 } 1796 1797 static void reducePriority() { 1798 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1799 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1800 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1801 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1802 .obtainMessage(WebCoreThread.REDUCE_PRIORITY)); 1803 } 1804 1805 static void resumePriority() { 1806 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1807 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1808 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1809 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1810 .obtainMessage(WebCoreThread.RESUME_PRIORITY)); 1811 } 1812 1813 static void pauseUpdatePicture(WebViewCore core) { 1814 // Note: there is one possible failure mode. If pauseUpdatePicture() is 1815 // called from UI thread while WEBKIT_DRAW is just pulled out of the 1816 // queue in WebCore thread to be executed. Then update won't be blocked. 1817 if (core != null) { 1818 synchronized (core) { 1819 core.mDrawIsPaused = true; 1820 if (core.mDrawIsScheduled) { 1821 core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW); 1822 } 1823 } 1824 } 1825 1826 } 1827 1828 static void resumeUpdatePicture(WebViewCore core) { 1829 if (core != null) { 1830 synchronized (core) { 1831 core.mDrawIsPaused = false; 1832 if (core.mDrawIsScheduled) { 1833 core.mDrawIsScheduled = false; 1834 core.contentDraw(); 1835 } 1836 } 1837 } 1838 } 1839 1840 ////////////////////////////////////////////////////////////////////////// 1841 1842 private void restoreState(int index) { 1843 WebBackForwardList list = mCallbackProxy.getBackForwardList(); 1844 int size = list.getSize(); 1845 for (int i = 0; i < size; i++) { 1846 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); 1847 } 1848 mBrowserFrame.mLoadInitFromJava = true; 1849 list.restoreIndex(mBrowserFrame.mNativeFrame, index); 1850 mBrowserFrame.mLoadInitFromJava = false; 1851 } 1852 1853 //------------------------------------------------------------------------- 1854 // Implement abstract methods in WebViewCore, native WebKit callback part 1855 //------------------------------------------------------------------------- 1856 1857 // called from JNI or WebView thread 1858 /* package */ void contentDraw() { 1859 // don't update the Picture until we have an initial width and finish 1860 // the first layout 1861 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { 1862 return; 1863 } 1864 // only fire an event if this is our first request 1865 synchronized (this) { 1866 if (mDrawIsScheduled) return; 1867 mDrawIsScheduled = true; 1868 if (mDrawIsPaused) return; 1869 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 1870 } 1871 } 1872 1873 // called by JNI 1874 private void contentScrollBy(int dx, int dy, boolean animate) { 1875 if (!mBrowserFrame.firstLayoutDone()) { 1876 // Will this happen? If yes, we need to do something here. 1877 return; 1878 } 1879 if (mWebView != null) { 1880 Message msg = Message.obtain(mWebView.mPrivateHandler, 1881 WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate)); 1882 if (mDrawIsScheduled) { 1883 mEventHub.sendMessage(Message.obtain(null, 1884 EventHub.MESSAGE_RELAY, msg)); 1885 } else { 1886 msg.sendToTarget(); 1887 } 1888 } 1889 } 1890 1891 // called by JNI 1892 private void contentScrollTo(int x, int y) { 1893 if (!mBrowserFrame.firstLayoutDone()) { 1894 /* 1895 * WebKit restore state will be called before didFirstLayout(), 1896 * remember the position as it has to be applied after restoring 1897 * zoom factor which is controlled by screenWidth. 1898 */ 1899 mRestoredX = x; 1900 mRestoredY = y; 1901 return; 1902 } 1903 if (mWebView != null) { 1904 Message msg = Message.obtain(mWebView.mPrivateHandler, 1905 WebView.SCROLL_TO_MSG_ID, x, y); 1906 if (mDrawIsScheduled) { 1907 mEventHub.sendMessage(Message.obtain(null, 1908 EventHub.MESSAGE_RELAY, msg)); 1909 } else { 1910 msg.sendToTarget(); 1911 } 1912 } 1913 } 1914 1915 // called by JNI 1916 private void contentSpawnScrollTo(int x, int y) { 1917 if (!mBrowserFrame.firstLayoutDone()) { 1918 /* 1919 * WebKit restore state will be called before didFirstLayout(), 1920 * remember the position as it has to be applied after restoring 1921 * zoom factor which is controlled by screenWidth. 1922 */ 1923 mRestoredX = x; 1924 mRestoredY = y; 1925 return; 1926 } 1927 if (mWebView != null) { 1928 Message msg = Message.obtain(mWebView.mPrivateHandler, 1929 WebView.SPAWN_SCROLL_TO_MSG_ID, x, y); 1930 if (mDrawIsScheduled) { 1931 mEventHub.sendMessage(Message.obtain(null, 1932 EventHub.MESSAGE_RELAY, msg)); 1933 } else { 1934 msg.sendToTarget(); 1935 } 1936 } 1937 } 1938 1939 // called by JNI 1940 private void sendNotifyProgressFinished() { 1941 sendUpdateTextEntry(); 1942 // as CacheManager can behave based on database transaction, we need to 1943 // call tick() to trigger endTransaction 1944 WebViewWorker.getHandler().removeMessages( 1945 WebViewWorker.MSG_CACHE_TRANSACTION_TICKER); 1946 WebViewWorker.getHandler().sendEmptyMessage( 1947 WebViewWorker.MSG_CACHE_TRANSACTION_TICKER); 1948 contentDraw(); 1949 } 1950 1951 /* Called by JNI. The coordinates are in doc coordinates, so they need to 1952 be scaled before they can be used by the view system, which happens 1953 in WebView since it (and its thread) know the current scale factor. 1954 */ 1955 private void sendViewInvalidate(int left, int top, int right, int bottom) { 1956 if (mWebView != null) { 1957 Message.obtain(mWebView.mPrivateHandler, 1958 WebView.INVAL_RECT_MSG_ID, 1959 new Rect(left, top, right, bottom)).sendToTarget(); 1960 } 1961 } 1962 1963 private static boolean mRepaintScheduled = false; 1964 1965 /* 1966 * Called by the WebView thread 1967 */ 1968 /* package */ void signalRepaintDone() { 1969 mRepaintScheduled = false; 1970 } 1971 1972 // called by JNI 1973 private void sendImmediateRepaint() { 1974 if (mWebView != null && !mRepaintScheduled) { 1975 mRepaintScheduled = true; 1976 Message.obtain(mWebView.mPrivateHandler, 1977 WebView.IMMEDIATE_REPAINT_MSG_ID).sendToTarget(); 1978 } 1979 } 1980 1981 // called by JNI 1982 private void setRootLayer(int layer) { 1983 if (mWebView != null) { 1984 Message.obtain(mWebView.mPrivateHandler, 1985 WebView.SET_ROOT_LAYER_MSG_ID, 1986 layer, 0).sendToTarget(); 1987 } 1988 } 1989 1990 /* package */ WebView getWebView() { 1991 return mWebView; 1992 } 1993 1994 private native void setViewportSettingsFromNative(); 1995 1996 // called by JNI 1997 private void didFirstLayout(boolean standardLoad) { 1998 if (DebugFlags.WEB_VIEW_CORE) { 1999 Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad); 2000 } 2001 2002 mBrowserFrame.didFirstLayout(); 2003 2004 if (mWebView == null) return; 2005 2006 boolean updateRestoreState = standardLoad || mRestoredScale > 0; 2007 setupViewport(updateRestoreState); 2008 // if updateRestoreState is true, ViewManager.postReadyToDrawAll() will 2009 // be called after the WebView restore the state. If updateRestoreState 2010 // is false, start to draw now as it is ready. 2011 if (!updateRestoreState) { 2012 mWebView.mViewManager.postReadyToDrawAll(); 2013 } 2014 2015 // reset the scroll position, the restored offset and scales 2016 mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY 2017 = mRestoredScale = mRestoredScreenWidthScale = 0; 2018 } 2019 2020 // called by JNI 2021 private void updateViewport() { 2022 // if updateViewport is called before first layout, wait until first 2023 // layout to update the viewport. In the rare case, this is called after 2024 // first layout, force an update as we have just parsed the viewport 2025 // meta tag. 2026 if (mBrowserFrame.firstLayoutDone()) { 2027 setupViewport(true); 2028 } 2029 } 2030 2031 private void setupViewport(boolean updateRestoreState) { 2032 // set the viewport settings from WebKit 2033 setViewportSettingsFromNative(); 2034 2035 // adjust the default scale to match the densityDpi 2036 float adjust = 1.0f; 2037 if (mViewportDensityDpi == -1) { 2038 if (WebView.DEFAULT_SCALE_PERCENT != 100) { 2039 adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f; 2040 } 2041 } else if (mViewportDensityDpi > 0) { 2042 adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi 2043 / mViewportDensityDpi; 2044 } 2045 int defaultScale = (int) (adjust * 100); 2046 2047 if (mViewportInitialScale > 0) { 2048 mViewportInitialScale *= adjust; 2049 } 2050 if (mViewportMinimumScale > 0) { 2051 mViewportMinimumScale *= adjust; 2052 } 2053 if (mViewportMaximumScale > 0) { 2054 mViewportMaximumScale *= adjust; 2055 } 2056 2057 // infer the values if they are not defined. 2058 if (mViewportWidth == 0) { 2059 if (mViewportInitialScale == 0) { 2060 mViewportInitialScale = defaultScale; 2061 } 2062 } 2063 if (mViewportUserScalable == false) { 2064 mViewportInitialScale = defaultScale; 2065 mViewportMinimumScale = defaultScale; 2066 mViewportMaximumScale = defaultScale; 2067 } 2068 if (mViewportMinimumScale > mViewportInitialScale 2069 && mViewportInitialScale != 0) { 2070 mViewportMinimumScale = mViewportInitialScale; 2071 } 2072 if (mViewportMaximumScale > 0 2073 && mViewportMaximumScale < mViewportInitialScale) { 2074 mViewportMaximumScale = mViewportInitialScale; 2075 } 2076 if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) { 2077 mViewportWidth = 0; 2078 } 2079 2080 // if mViewportWidth is 0, it means device-width, always update. 2081 if (mViewportWidth != 0 && !updateRestoreState) { 2082 RestoreState restoreState = new RestoreState(); 2083 restoreState.mMinScale = mViewportMinimumScale / 100.0f; 2084 restoreState.mMaxScale = mViewportMaximumScale / 100.0f; 2085 restoreState.mDefaultScale = adjust; 2086 // as mViewportWidth is not 0, it is not mobile site. 2087 restoreState.mMobileSite = false; 2088 // for non-mobile site, we don't need minPrefWidth, set it as 0 2089 restoreState.mScrollX = 0; 2090 Message.obtain(mWebView.mPrivateHandler, 2091 WebView.UPDATE_ZOOM_RANGE, restoreState).sendToTarget(); 2092 return; 2093 } 2094 2095 // now notify webview 2096 // webViewWidth refers to the width in the view system 2097 int webViewWidth; 2098 // viewportWidth refers to the width in the document system 2099 int viewportWidth = mCurrentViewWidth; 2100 if (viewportWidth == 0) { 2101 // this may happen when WebView just starts. This is not perfect as 2102 // we call WebView method from WebCore thread. But not perfect 2103 // reference is better than no reference. 2104 webViewWidth = mWebView.getViewWidth(); 2105 viewportWidth = (int) (webViewWidth / adjust); 2106 if (viewportWidth == 0) { 2107 Log.w(LOGTAG, "Can't get the viewWidth after the first layout"); 2108 } 2109 } else { 2110 webViewWidth = Math.round(viewportWidth * mCurrentViewScale); 2111 } 2112 mRestoreState = new RestoreState(); 2113 mRestoreState.mMinScale = mViewportMinimumScale / 100.0f; 2114 mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f; 2115 mRestoreState.mDefaultScale = adjust; 2116 mRestoreState.mScrollX = mRestoredX; 2117 mRestoreState.mScrollY = mRestoredY; 2118 mRestoreState.mMobileSite = (0 == mViewportWidth); 2119 if (mRestoredScale > 0) { 2120 mRestoreState.mViewScale = mRestoredScale / 100.0f; 2121 if (mRestoredScreenWidthScale > 0) { 2122 mRestoreState.mTextWrapScale = 2123 mRestoredScreenWidthScale / 100.0f; 2124 } else { 2125 mRestoreState.mTextWrapScale = mRestoreState.mViewScale; 2126 } 2127 } else { 2128 if (mViewportInitialScale > 0) { 2129 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 2130 mViewportInitialScale / 100.0f; 2131 } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) { 2132 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 2133 (float) webViewWidth / mViewportWidth; 2134 } else { 2135 mRestoreState.mTextWrapScale = adjust; 2136 // 0 will trigger WebView to turn on zoom overview mode 2137 mRestoreState.mViewScale = 0; 2138 } 2139 } 2140 2141 if (mWebView.mHeightCanMeasure) { 2142 // Trick to ensure that the Picture has the exact height for the 2143 // content by forcing to layout with 0 height after the page is 2144 // ready, which is indicated by didFirstLayout. This is essential to 2145 // get rid of the white space in the GMail which uses WebView for 2146 // message view. 2147 mWebView.mLastHeightSent = 0; 2148 // Send a negative scale to indicate that WebCore should reuse 2149 // the current scale 2150 WebView.ViewSizeData data = new WebView.ViewSizeData(); 2151 data.mWidth = mWebView.mLastWidthSent; 2152 data.mHeight = 0; 2153 // if mHeightCanMeasure is true, getUseWideViewPort() can't be 2154 // true. It is safe to use mWidth for mTextWrapWidth. 2155 data.mTextWrapWidth = data.mWidth; 2156 data.mScale = -1.0f; 2157 data.mIgnoreHeight = false; 2158 data.mAnchorX = data.mAnchorY = 0; 2159 // send VIEW_SIZE_CHANGED to the front of the queue so that we can 2160 // avoid pushing the wrong picture to the WebView side. If there is 2161 // a VIEW_SIZE_CHANGED in the queue, probably from WebView side, 2162 // ignore it as we have a new size. If we leave VIEW_SIZE_CHANGED 2163 // in the queue, as mLastHeightSent has been updated here, we may 2164 // miss the requestLayout in WebView side after the new picture. 2165 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2166 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 2167 EventHub.VIEW_SIZE_CHANGED, data)); 2168 } else if (mSettings.getUseWideViewPort()) { 2169 if (viewportWidth == 0) { 2170 // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView 2171 // to WebViewCore 2172 mWebView.mLastWidthSent = 0; 2173 } else { 2174 WebView.ViewSizeData data = new WebView.ViewSizeData(); 2175 // mViewScale as 0 means it is in zoom overview mode. So we don't 2176 // know the exact scale. If mRestoredScale is non-zero, use it; 2177 // otherwise just use mTextWrapScale as the initial scale. 2178 data.mScale = mRestoreState.mViewScale == 0 2179 ? (mRestoredScale > 0 ? mRestoredScale / 100.0f 2180 : mRestoreState.mTextWrapScale) 2181 : mRestoreState.mViewScale; 2182 if (DebugFlags.WEB_VIEW_CORE) { 2183 Log.v(LOGTAG, "setupViewport" 2184 + " mRestoredScale=" + mRestoredScale 2185 + " mViewScale=" + mRestoreState.mViewScale 2186 + " mTextWrapScale=" + mRestoreState.mTextWrapScale 2187 ); 2188 } 2189 data.mWidth = Math.round(webViewWidth / data.mScale); 2190 // We may get a call here when mCurrentViewHeight == 0 if webcore completes the 2191 // first layout before we sync our webview dimensions to it. In that case, we 2192 // request the real height of the webview. This is not a perfect solution as we 2193 // are calling a WebView method from the WebCore thread. But this is preferable 2194 // to syncing an incorrect height. 2195 data.mHeight = mCurrentViewHeight == 0 ? 2196 Math.round(mWebView.getViewHeight() / data.mScale) 2197 : mCurrentViewHeight * data.mWidth / viewportWidth; 2198 data.mTextWrapWidth = Math.round(webViewWidth 2199 / mRestoreState.mTextWrapScale); 2200 data.mIgnoreHeight = false; 2201 data.mAnchorX = data.mAnchorY = 0; 2202 // send VIEW_SIZE_CHANGED to the front of the queue so that we 2203 // can avoid pushing the wrong picture to the WebView side. 2204 mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); 2205 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 2206 EventHub.VIEW_SIZE_CHANGED, data)); 2207 } 2208 } 2209 } 2210 2211 // called by JNI 2212 private void restoreScale(int scale) { 2213 if (mBrowserFrame.firstLayoutDone() == false) { 2214 mRestoredScale = scale; 2215 } 2216 } 2217 2218 // called by JNI 2219 private void restoreScreenWidthScale(int scale) { 2220 if (!mSettings.getUseWideViewPort()) { 2221 return; 2222 } 2223 2224 if (mBrowserFrame.firstLayoutDone() == false) { 2225 mRestoredScreenWidthScale = scale; 2226 } 2227 } 2228 2229 // called by JNI 2230 private void needTouchEvents(boolean need) { 2231 if (mWebView != null) { 2232 Message.obtain(mWebView.mPrivateHandler, 2233 WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0) 2234 .sendToTarget(); 2235 } 2236 } 2237 2238 // called by JNI 2239 private void updateTextfield(int ptr, boolean changeToPassword, 2240 String text, int textGeneration) { 2241 if (mWebView != null) { 2242 Message msg = Message.obtain(mWebView.mPrivateHandler, 2243 WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 2244 textGeneration, text); 2245 msg.getData().putBoolean("password", changeToPassword); 2246 msg.sendToTarget(); 2247 } 2248 } 2249 2250 // called by JNI 2251 private void updateTextSelection(int pointer, int start, int end, 2252 int textGeneration) { 2253 if (mWebView != null) { 2254 Message.obtain(mWebView.mPrivateHandler, 2255 WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration, 2256 new TextSelectionData(start, end)).sendToTarget(); 2257 } 2258 } 2259 2260 // called by JNI 2261 private void clearTextEntry() { 2262 if (mWebView == null) return; 2263 Message.obtain(mWebView.mPrivateHandler, 2264 WebView.CLEAR_TEXT_ENTRY).sendToTarget(); 2265 } 2266 2267 // called by JNI 2268 private void sendFindAgain() { 2269 if (mWebView == null) return; 2270 Message.obtain(mWebView.mPrivateHandler, 2271 WebView.FIND_AGAIN).sendToTarget(); 2272 } 2273 2274 private native void nativeUpdateFrameCacheIfLoading(); 2275 private native String nativeRequestLabel(int framePtr, int nodePtr); 2276 /** 2277 * Scroll the focused textfield to (xPercent, y) in document space 2278 */ 2279 private native void nativeScrollFocusedTextInput(float xPercent, int y); 2280 2281 // these must be in document space (i.e. not scaled/zoomed). 2282 private native void nativeSetScrollOffset(int gen, int dx, int dy); 2283 2284 private native void nativeSetGlobalBounds(int x, int y, int w, int h); 2285 2286 // called by JNI 2287 private void requestListBox(String[] array, int[] enabledArray, 2288 int[] selectedArray) { 2289 if (mWebView != null) { 2290 mWebView.requestListBox(array, enabledArray, selectedArray); 2291 } 2292 } 2293 2294 // called by JNI 2295 private void requestListBox(String[] array, int[] enabledArray, 2296 int selection) { 2297 if (mWebView != null) { 2298 mWebView.requestListBox(array, enabledArray, selection); 2299 } 2300 2301 } 2302 2303 // called by JNI 2304 private void requestKeyboardWithSelection(int pointer, int selStart, 2305 int selEnd, int textGeneration) { 2306 if (mWebView != null) { 2307 Message.obtain(mWebView.mPrivateHandler, 2308 WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer, 2309 textGeneration, new TextSelectionData(selStart, selEnd)) 2310 .sendToTarget(); 2311 } 2312 } 2313 2314 // called by JNI 2315 private void requestKeyboard(boolean showKeyboard) { 2316 if (mWebView != null) { 2317 Message.obtain(mWebView.mPrivateHandler, 2318 WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0) 2319 .sendToTarget(); 2320 } 2321 } 2322 2323 // called by JNI 2324 private Context getContext() { 2325 return mContext; 2326 } 2327 2328 // called by JNI 2329 private Class<?> getPluginClass(String libName, String clsName) { 2330 2331 if (mWebView == null) { 2332 return null; 2333 } 2334 2335 PluginManager pluginManager = PluginManager.getInstance(null); 2336 2337 String pkgName = pluginManager.getPluginsAPKName(libName); 2338 if (pkgName == null) { 2339 Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); 2340 return null; 2341 } 2342 2343 try { 2344 return pluginManager.getPluginClass(pkgName, clsName); 2345 } catch (NameNotFoundException e) { 2346 Log.e(LOGTAG, "Unable to find plugin classloader for the apk (" + pkgName + ")"); 2347 } catch (ClassNotFoundException e) { 2348 Log.e(LOGTAG, "Unable to find plugin class (" + clsName + 2349 ") in the apk (" + pkgName + ")"); 2350 } 2351 2352 return null; 2353 } 2354 2355 // called by JNI. PluginWidget function to launch a full-screen view using a 2356 // View object provided by the plugin class. 2357 private void showFullScreenPlugin(ViewManager.ChildView childView, int npp) { 2358 if (mWebView == null) { 2359 return; 2360 } 2361 2362 Message message = mWebView.mPrivateHandler.obtainMessage(WebView.SHOW_FULLSCREEN); 2363 message.obj = childView.mView; 2364 message.arg1 = npp; 2365 message.sendToTarget(); 2366 } 2367 2368 // called by JNI 2369 private void hideFullScreenPlugin() { 2370 if (mWebView == null) { 2371 return; 2372 } 2373 mWebView.mPrivateHandler.obtainMessage(WebView.HIDE_FULLSCREEN) 2374 .sendToTarget(); 2375 } 2376 2377 // called by JNI. PluginWidget functions for creating an embedded View for 2378 // the surface drawing model. 2379 private ViewManager.ChildView addSurface(View pluginView, int x, int y, 2380 int width, int height) { 2381 if (mWebView == null) { 2382 return null; 2383 } 2384 2385 if (pluginView == null) { 2386 Log.e(LOGTAG, "Attempted to add an empty plugin view to the view hierarchy"); 2387 return null; 2388 } 2389 2390 // ensures the view system knows the view can redraw itself 2391 pluginView.setWillNotDraw(false); 2392 2393 if(pluginView instanceof SurfaceView) 2394 ((SurfaceView)pluginView).setZOrderOnTop(true); 2395 2396 ViewManager.ChildView view = mWebView.mViewManager.createView(); 2397 view.mView = pluginView; 2398 view.attachView(x, y, width, height); 2399 return view; 2400 } 2401 2402 private void updateSurface(ViewManager.ChildView childView, int x, int y, 2403 int width, int height) { 2404 childView.attachView(x, y, width, height); 2405 } 2406 2407 private void destroySurface(ViewManager.ChildView childView) { 2408 childView.removeView(); 2409 } 2410 2411 // called by JNI 2412 static class ShowRectData { 2413 int mLeft; 2414 int mTop; 2415 int mWidth; 2416 int mHeight; 2417 int mContentWidth; 2418 int mContentHeight; 2419 float mXPercentInDoc; 2420 float mXPercentInView; 2421 float mYPercentInDoc; 2422 float mYPercentInView; 2423 } 2424 2425 private void showRect(int left, int top, int width, int height, 2426 int contentWidth, int contentHeight, float xPercentInDoc, 2427 float xPercentInView, float yPercentInDoc, float yPercentInView) { 2428 if (mWebView != null) { 2429 ShowRectData data = new ShowRectData(); 2430 data.mLeft = left; 2431 data.mTop = top; 2432 data.mWidth = width; 2433 data.mHeight = height; 2434 data.mContentWidth = contentWidth; 2435 data.mContentHeight = contentHeight; 2436 data.mXPercentInDoc = xPercentInDoc; 2437 data.mXPercentInView = xPercentInView; 2438 data.mYPercentInDoc = yPercentInDoc; 2439 data.mYPercentInView = yPercentInView; 2440 Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID, 2441 data).sendToTarget(); 2442 } 2443 } 2444 2445 // called by JNI 2446 private void centerFitRect(int x, int y, int width, int height) { 2447 if (mWebView == null) { 2448 return; 2449 } 2450 mWebView.mPrivateHandler.obtainMessage(WebView.CENTER_FIT_RECT, 2451 new Rect(x, y, x + width, y + height)).sendToTarget(); 2452 } 2453 2454 private native void nativePause(); 2455 private native void nativeResume(); 2456 private native void nativeFreeMemory(); 2457 private native void nativeFullScreenPluginHidden(int npp); 2458 private native boolean nativeValidNodeAndBounds(int frame, int node, 2459 Rect bounds); 2460 2461} 2462