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