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