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