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