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