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