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