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