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