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