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