WebViewCore.java revision dee76be487fcdc975877f85221a693b87b9669a7
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 mSettings.onDestroyed(); 857 mNativeClass = 0; 858 } 859 break; 860 861 case UPDATE_FRAME_CACHE_IF_LOADING: 862 nativeUpdateFrameCacheIfLoading(); 863 break; 864 865 case SCROLL_TEXT_INPUT: 866 nativeScrollFocusedTextInput(msg.arg1, msg.arg2); 867 break; 868 869 case LOAD_URL: 870 loadUrl((String) msg.obj); 871 break; 872 873 case POST_URL: { 874 PostUrlData param = (PostUrlData) msg.obj; 875 mBrowserFrame.postUrl(param.mUrl, param.mPostData); 876 break; 877 } 878 case LOAD_DATA: 879 BaseUrlData loadParams = (BaseUrlData) msg.obj; 880 String baseUrl = loadParams.mBaseUrl; 881 if (baseUrl != null) { 882 int i = baseUrl.indexOf(':'); 883 if (i > 0) { 884 /* 885 * In 1.0, {@link 886 * WebView#loadDataWithBaseURL} can access 887 * local asset files as long as the data is 888 * valid. In the new WebKit, the restriction 889 * is tightened. To be compatible with 1.0, 890 * we automatically add the scheme of the 891 * baseUrl for local access as long as it is 892 * not http(s)/ftp(s)/about/javascript 893 */ 894 String scheme = baseUrl.substring(0, i); 895 if (!scheme.startsWith("http") && 896 !scheme.startsWith("ftp") && 897 !scheme.startsWith("about") && 898 !scheme.startsWith("javascript")) { 899 nativeRegisterURLSchemeAsLocal(scheme); 900 } 901 } 902 } 903 mBrowserFrame.loadData(baseUrl, 904 loadParams.mData, 905 loadParams.mMimeType, 906 loadParams.mEncoding, 907 loadParams.mFailUrl); 908 break; 909 910 case STOP_LOADING: 911 // If the WebCore has committed the load, but not 912 // finished the first layout yet, we need to set 913 // first layout done to trigger the interpreted side sync 914 // up with native side 915 if (mBrowserFrame.committed() 916 && !mBrowserFrame.firstLayoutDone()) { 917 mBrowserFrame.didFirstLayout(); 918 } 919 // Do this after syncing up the layout state. 920 stopLoading(); 921 break; 922 923 case RELOAD: 924 mBrowserFrame.reload(false); 925 break; 926 927 case KEY_DOWN: 928 key((KeyEvent) msg.obj, true); 929 break; 930 931 case KEY_UP: 932 key((KeyEvent) msg.obj, false); 933 break; 934 935 case CLICK: 936 nativeClick(msg.arg1, msg.arg2); 937 break; 938 939 case VIEW_SIZE_CHANGED: { 940 WebView.ViewSizeData data = 941 (WebView.ViewSizeData) msg.obj; 942 viewSizeChanged(data.mWidth, data.mHeight, 943 data.mTextWrapWidth, data.mScale, 944 data.mIgnoreHeight); 945 break; 946 } 947 case SET_SCROLL_OFFSET: 948 // note: these are in document coordinates 949 // (inv-zoom) 950 Point pt = (Point) msg.obj; 951 nativeSetScrollOffset(msg.arg1, pt.x, pt.y); 952 break; 953 954 case SET_GLOBAL_BOUNDS: 955 Rect r = (Rect) msg.obj; 956 nativeSetGlobalBounds(r.left, r.top, r.width(), 957 r.height()); 958 break; 959 960 case GO_BACK_FORWARD: 961 // If it is a standard load and the load is not 962 // committed yet, we interpret BACK as RELOAD 963 if (!mBrowserFrame.committed() && msg.arg1 == -1 && 964 (mBrowserFrame.loadType() == 965 BrowserFrame.FRAME_LOADTYPE_STANDARD)) { 966 mBrowserFrame.reload(true); 967 } else { 968 mBrowserFrame.goBackOrForward(msg.arg1); 969 } 970 break; 971 972 case RESTORE_STATE: 973 stopLoading(); 974 restoreState(msg.arg1); 975 break; 976 977 case PAUSE_TIMERS: 978 mSavedPriority = Process.getThreadPriority(mTid); 979 Process.setThreadPriority(mTid, 980 Process.THREAD_PRIORITY_BACKGROUND); 981 pauseTimers(); 982 if (CacheManager.disableTransaction()) { 983 WebCoreThread.mCacheTickersBlocked = true; 984 sWebCoreHandler.removeMessages( 985 WebCoreThread.CACHE_TICKER); 986 } 987 break; 988 989 case RESUME_TIMERS: 990 Process.setThreadPriority(mTid, mSavedPriority); 991 resumeTimers(); 992 if (CacheManager.enableTransaction()) { 993 WebCoreThread.mCacheTickersBlocked = false; 994 sWebCoreHandler.sendMessageDelayed( 995 sWebCoreHandler.obtainMessage( 996 WebCoreThread.CACHE_TICKER), 997 WebCoreThread.CACHE_TICKER_INTERVAL); 998 } 999 break; 1000 1001 case ON_PAUSE: 1002 nativePause(); 1003 break; 1004 1005 case ON_RESUME: 1006 nativeResume(); 1007 break; 1008 1009 case FREE_MEMORY: 1010 clearCache(false); 1011 nativeFreeMemory(); 1012 break; 1013 1014 case PLUGIN_STATE: 1015 PluginStateData psd = (PluginStateData) msg.obj; 1016 nativeUpdatePluginState(psd.mFrame, psd.mNode, psd.mState); 1017 break; 1018 1019 case SET_NETWORK_STATE: 1020 if (BrowserFrame.sJavaBridge == null) { 1021 throw new IllegalStateException("No WebView " + 1022 "has been created in this process!"); 1023 } 1024 BrowserFrame.sJavaBridge 1025 .setNetworkOnLine(msg.arg1 == 1); 1026 break; 1027 1028 case CLEAR_CACHE: 1029 clearCache(msg.arg1 == 1); 1030 break; 1031 1032 case CLEAR_HISTORY: 1033 mCallbackProxy.getBackForwardList(). 1034 close(mBrowserFrame.mNativeFrame); 1035 break; 1036 1037 case REPLACE_TEXT: 1038 ReplaceTextData rep = (ReplaceTextData) msg.obj; 1039 nativeReplaceTextfieldText(msg.arg1, msg.arg2, 1040 rep.mReplace, rep.mNewStart, rep.mNewEnd, 1041 rep.mTextGeneration); 1042 break; 1043 1044 case PASS_TO_JS: { 1045 JSKeyData jsData = (JSKeyData) msg.obj; 1046 KeyEvent evt = jsData.mEvent; 1047 int keyCode = evt.getKeyCode(); 1048 int keyValue = evt.getUnicodeChar(); 1049 int generation = msg.arg1; 1050 passToJs(generation, 1051 jsData.mCurrentText, 1052 keyCode, 1053 keyValue, 1054 evt.isDown(), 1055 evt.isShiftPressed(), evt.isAltPressed(), 1056 evt.isSymPressed()); 1057 break; 1058 } 1059 1060 case SAVE_DOCUMENT_STATE: { 1061 CursorData cDat = (CursorData) msg.obj; 1062 nativeSaveDocumentState(cDat.mFrame); 1063 break; 1064 } 1065 1066 case CLEAR_SSL_PREF_TABLE: 1067 Network.getInstance(mContext) 1068 .clearUserSslPrefTable(); 1069 break; 1070 1071 case TOUCH_UP: 1072 TouchUpData touchUpData = (TouchUpData) msg.obj; 1073 nativeTouchUp(touchUpData.mMoveGeneration, 1074 touchUpData.mFrame, touchUpData.mNode, 1075 touchUpData.mX, touchUpData.mY); 1076 break; 1077 1078 case TOUCH_EVENT: { 1079 TouchEventData ted = (TouchEventData) msg.obj; 1080 Message.obtain( 1081 mWebView.mPrivateHandler, 1082 WebView.PREVENT_TOUCH_ID, ted.mAction, 1083 nativeHandleTouchEvent(ted.mAction, ted.mX, 1084 ted.mY) ? 1 : 0).sendToTarget(); 1085 break; 1086 } 1087 1088 case SET_ACTIVE: 1089 nativeSetFocusControllerActive(msg.arg1 == 1); 1090 break; 1091 1092 case ADD_JS_INTERFACE: 1093 JSInterfaceData jsData = (JSInterfaceData) msg.obj; 1094 mBrowserFrame.addJavascriptInterface(jsData.mObject, 1095 jsData.mInterfaceName); 1096 break; 1097 1098 case REQUEST_EXT_REPRESENTATION: 1099 mBrowserFrame.externalRepresentation( 1100 (Message) msg.obj); 1101 break; 1102 1103 case REQUEST_DOC_AS_TEXT: 1104 mBrowserFrame.documentAsText((Message) msg.obj); 1105 break; 1106 1107 case SET_MOVE_MOUSE: 1108 CursorData cursorData = (CursorData) msg.obj; 1109 nativeMoveMouse(cursorData.mFrame, 1110 cursorData.mX, cursorData.mY); 1111 break; 1112 1113 case SET_MOVE_MOUSE_IF_LATEST: 1114 CursorData cData = (CursorData) msg.obj; 1115 nativeMoveMouseIfLatest(cData.mMoveGeneration, 1116 cData.mFrame, 1117 cData.mX, cData.mY); 1118 break; 1119 1120 case REQUEST_CURSOR_HREF: { 1121 Message hrefMsg = (Message) msg.obj; 1122 String res = nativeRetrieveHref(msg.arg1, msg.arg2); 1123 hrefMsg.getData().putString("url", res); 1124 hrefMsg.sendToTarget(); 1125 break; 1126 } 1127 1128 case UPDATE_CACHE_AND_TEXT_ENTRY: 1129 nativeUpdateFrameCache(); 1130 // FIXME: this should provide a minimal rectangle 1131 if (mWebView != null) { 1132 mWebView.postInvalidate(); 1133 } 1134 sendUpdateTextEntry(); 1135 break; 1136 1137 case DOC_HAS_IMAGES: 1138 Message imageResult = (Message) msg.obj; 1139 imageResult.arg1 = 1140 mBrowserFrame.documentHasImages() ? 1 : 0; 1141 imageResult.sendToTarget(); 1142 break; 1143 1144 case DELETE_SELECTION: 1145 TextSelectionData deleteSelectionData 1146 = (TextSelectionData) msg.obj; 1147 nativeDeleteSelection(deleteSelectionData.mStart, 1148 deleteSelectionData.mEnd, msg.arg1); 1149 break; 1150 1151 case SET_SELECTION: 1152 nativeSetSelection(msg.arg1, msg.arg2); 1153 break; 1154 1155 case LISTBOX_CHOICES: 1156 SparseBooleanArray choices = (SparseBooleanArray) 1157 msg.obj; 1158 int choicesSize = msg.arg1; 1159 boolean[] choicesArray = new boolean[choicesSize]; 1160 for (int c = 0; c < choicesSize; c++) { 1161 choicesArray[c] = choices.get(c); 1162 } 1163 nativeSendListBoxChoices(choicesArray, 1164 choicesSize); 1165 break; 1166 1167 case SINGLE_LISTBOX_CHOICE: 1168 nativeSendListBoxChoice(msg.arg1); 1169 break; 1170 1171 case SET_BACKGROUND_COLOR: 1172 nativeSetBackgroundColor(msg.arg1); 1173 break; 1174 1175 case GET_SELECTION: 1176 String str = nativeGetSelection((Region) msg.obj); 1177 Message.obtain(mWebView.mPrivateHandler 1178 , WebView.UPDATE_CLIPBOARD, str) 1179 .sendToTarget(); 1180 break; 1181 1182 case DUMP_DOMTREE: 1183 nativeDumpDomTree(msg.arg1 == 1); 1184 break; 1185 1186 case DUMP_RENDERTREE: 1187 nativeDumpRenderTree(msg.arg1 == 1); 1188 break; 1189 1190 case DUMP_NAVTREE: 1191 nativeDumpNavTree(); 1192 break; 1193 1194 case SET_JS_FLAGS: 1195 nativeSetJsFlags((String)msg.obj); 1196 break; 1197 1198 case GEOLOCATION_PERMISSIONS_PROVIDE: 1199 GeolocationPermissionsData data = 1200 (GeolocationPermissionsData) msg.obj; 1201 nativeGeolocationPermissionsProvide(data.mOrigin, 1202 data.mAllow, data.mRemember); 1203 break; 1204 1205 case SYNC_SCROLL: 1206 mWebkitScrollX = msg.arg1; 1207 mWebkitScrollY = msg.arg2; 1208 break; 1209 1210 case SPLIT_PICTURE_SET: 1211 nativeSplitContent(); 1212 mSplitPictureIsScheduled = false; 1213 break; 1214 1215 case CLEAR_CONTENT: 1216 // Clear the view so that onDraw() will draw nothing 1217 // but white background 1218 // (See public method WebView.clearView) 1219 nativeClearContent(); 1220 break; 1221 1222 case MESSAGE_RELAY: 1223 if (msg.obj instanceof Message) { 1224 ((Message) msg.obj).sendToTarget(); 1225 } 1226 break; 1227 } 1228 } 1229 }; 1230 // Take all queued messages and resend them to the new handler. 1231 synchronized (this) { 1232 int size = mMessages.size(); 1233 for (int i = 0; i < size; i++) { 1234 mHandler.sendMessage(mMessages.get(i)); 1235 } 1236 mMessages = null; 1237 } 1238 } 1239 1240 /** 1241 * Send a message internally to the queue or to the handler 1242 */ 1243 private synchronized void sendMessage(Message msg) { 1244 if (mBlockMessages) { 1245 return; 1246 } 1247 if (mMessages != null) { 1248 mMessages.add(msg); 1249 } else { 1250 mHandler.sendMessage(msg); 1251 } 1252 } 1253 1254 private synchronized void removeMessages(int what) { 1255 if (mBlockMessages) { 1256 return; 1257 } 1258 if (what == EventHub.WEBKIT_DRAW) { 1259 mDrawIsScheduled = false; 1260 } 1261 if (mMessages != null) { 1262 Log.w(LOGTAG, "Not supported in this case."); 1263 } else { 1264 mHandler.removeMessages(what); 1265 } 1266 } 1267 1268 private synchronized boolean hasMessages(int what) { 1269 if (mBlockMessages) { 1270 return false; 1271 } 1272 if (mMessages != null) { 1273 Log.w(LOGTAG, "hasMessages() is not supported in this case."); 1274 return false; 1275 } else { 1276 return mHandler.hasMessages(what); 1277 } 1278 } 1279 1280 private synchronized void sendMessageDelayed(Message msg, long delay) { 1281 if (mBlockMessages) { 1282 return; 1283 } 1284 mHandler.sendMessageDelayed(msg, delay); 1285 } 1286 1287 /** 1288 * Send a message internally to the front of the queue. 1289 */ 1290 private synchronized void sendMessageAtFrontOfQueue(Message msg) { 1291 if (mBlockMessages) { 1292 return; 1293 } 1294 if (mMessages != null) { 1295 mMessages.add(0, msg); 1296 } else { 1297 mHandler.sendMessageAtFrontOfQueue(msg); 1298 } 1299 } 1300 1301 /** 1302 * Remove all the messages. 1303 */ 1304 private synchronized void removeMessages() { 1305 // reset mDrawIsScheduled flag as WEBKIT_DRAW may be removed 1306 mDrawIsScheduled = false; 1307 mSplitPictureIsScheduled = false; 1308 if (mMessages != null) { 1309 mMessages.clear(); 1310 } else { 1311 mHandler.removeCallbacksAndMessages(null); 1312 } 1313 } 1314 1315 /** 1316 * Block sending messages to the EventHub. 1317 */ 1318 private synchronized void blockMessages() { 1319 mBlockMessages = true; 1320 } 1321 } 1322 1323 //------------------------------------------------------------------------- 1324 // Methods called by host activity (in the same thread) 1325 //------------------------------------------------------------------------- 1326 1327 void stopLoading() { 1328 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading"); 1329 if (mBrowserFrame != null) { 1330 mBrowserFrame.stopLoading(); 1331 } 1332 } 1333 1334 //------------------------------------------------------------------------- 1335 // Methods called by WebView 1336 // If it refers to local variable, it needs synchronized(). 1337 // If it needs WebCore, it has to send message. 1338 //------------------------------------------------------------------------- 1339 1340 void sendMessage(Message msg) { 1341 mEventHub.sendMessage(msg); 1342 } 1343 1344 void sendMessage(int what) { 1345 mEventHub.sendMessage(Message.obtain(null, what)); 1346 } 1347 1348 void sendMessage(int what, Object obj) { 1349 mEventHub.sendMessage(Message.obtain(null, what, obj)); 1350 } 1351 1352 void sendMessage(int what, int arg1) { 1353 // just ignore the second argument (make it 0) 1354 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0)); 1355 } 1356 1357 void sendMessage(int what, int arg1, int arg2) { 1358 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2)); 1359 } 1360 1361 void sendMessage(int what, int arg1, Object obj) { 1362 // just ignore the second argument (make it 0) 1363 mEventHub.sendMessage(Message.obtain(null, what, arg1, 0, obj)); 1364 } 1365 1366 void sendMessage(int what, int arg1, int arg2, Object obj) { 1367 mEventHub.sendMessage(Message.obtain(null, what, arg1, arg2, obj)); 1368 } 1369 1370 void sendMessageDelayed(int what, Object obj, long delay) { 1371 mEventHub.sendMessageDelayed(Message.obtain(null, what, obj), delay); 1372 } 1373 1374 void removeMessages(int what) { 1375 mEventHub.removeMessages(what); 1376 } 1377 1378 void removeMessages() { 1379 mEventHub.removeMessages(); 1380 } 1381 1382 /** 1383 * Removes pending messages and trigger a DESTROY message to send to 1384 * WebCore. 1385 * Called from UI thread. 1386 */ 1387 void destroy() { 1388 // We don't want anyone to post a message between removing pending 1389 // messages and sending the destroy message. 1390 synchronized (mEventHub) { 1391 // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to 1392 // be preserved even the WebView is destroyed. 1393 // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS 1394 boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS); 1395 boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS); 1396 mEventHub.removeMessages(); 1397 mEventHub.sendMessageAtFrontOfQueue( 1398 Message.obtain(null, EventHub.DESTROY)); 1399 if (hasPause) { 1400 mEventHub.sendMessageAtFrontOfQueue( 1401 Message.obtain(null, EventHub.PAUSE_TIMERS)); 1402 } 1403 if (hasResume) { 1404 mEventHub.sendMessageAtFrontOfQueue( 1405 Message.obtain(null, EventHub.RESUME_TIMERS)); 1406 } 1407 mEventHub.blockMessages(); 1408 mWebView = null; 1409 } 1410 } 1411 1412 //------------------------------------------------------------------------- 1413 // WebViewCore private methods 1414 //------------------------------------------------------------------------- 1415 1416 private void clearCache(boolean includeDiskFiles) { 1417 mBrowserFrame.clearCache(); 1418 if (includeDiskFiles) { 1419 CacheManager.removeAllCacheFiles(); 1420 } 1421 } 1422 1423 private void loadUrl(String url) { 1424 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url); 1425 mBrowserFrame.loadUrl(url); 1426 } 1427 1428 private void key(KeyEvent evt, boolean isDown) { 1429 if (DebugFlags.WEB_VIEW_CORE) { 1430 Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", " 1431 + evt); 1432 } 1433 int keyCode = evt.getKeyCode(); 1434 if (!nativeKey(keyCode, evt.getUnicodeChar(), 1435 evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(), 1436 evt.isSymPressed(), 1437 isDown) && keyCode != KeyEvent.KEYCODE_ENTER) { 1438 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP 1439 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) { 1440 if (DebugFlags.WEB_VIEW_CORE) { 1441 Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode); 1442 } 1443 if (mWebView != null && evt.isDown()) { 1444 Message.obtain(mWebView.mPrivateHandler, 1445 WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget(); 1446 } 1447 return; 1448 } 1449 // bubble up the event handling 1450 // but do not bubble up the ENTER key, which would open the search 1451 // bar without any text. 1452 mCallbackProxy.onUnhandledKeyEvent(evt); 1453 } 1454 } 1455 1456 // These values are used to avoid requesting a layout based on old values 1457 private int mCurrentViewWidth = 0; 1458 private int mCurrentViewHeight = 0; 1459 private float mCurrentViewScale = 1.0f; 1460 1461 // notify webkit that our virtual view size changed size (after inv-zoom) 1462 private void viewSizeChanged(int w, int h, int textwrapWidth, float scale, 1463 boolean ignoreHeight) { 1464 if (DebugFlags.WEB_VIEW_CORE) { 1465 Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h 1466 + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale); 1467 } 1468 if (w == 0) { 1469 Log.w(LOGTAG, "skip viewSizeChanged as w is 0"); 1470 return; 1471 } 1472 int width = w; 1473 if (mSettings.getUseWideViewPort()) { 1474 if (mViewportWidth == -1) { 1475 if (mSettings.getLayoutAlgorithm() == 1476 WebSettings.LayoutAlgorithm.NORMAL) { 1477 width = DEFAULT_VIEWPORT_WIDTH; 1478 } else { 1479 /* 1480 * if a page's minimum preferred width is wider than the 1481 * given "w", use it instead to get better layout result. If 1482 * we start a page with MAX_ZOOM_WIDTH, "w" will be always 1483 * wider. If we start a page with screen width, due to the 1484 * delay between {@link #didFirstLayout} and 1485 * {@link #viewSizeChanged}, 1486 * {@link #nativeGetContentMinPrefWidth} will return a more 1487 * accurate value than initial 0 to result a better layout. 1488 * In the worse case, the native width will be adjusted when 1489 * next zoom or screen orientation change happens. 1490 */ 1491 width = Math.max(w, Math.max(DEFAULT_VIEWPORT_WIDTH, 1492 nativeGetContentMinPrefWidth())); 1493 } 1494 } else { 1495 width = Math.max(w, mViewportWidth); 1496 } 1497 } 1498 nativeSetSize(width, width == w ? h : Math.round((float) width * h / w), 1499 textwrapWidth, scale, w, h, ignoreHeight); 1500 // Remember the current width and height 1501 boolean needInvalidate = (mCurrentViewWidth == 0); 1502 mCurrentViewWidth = w; 1503 mCurrentViewHeight = h; 1504 mCurrentViewScale = scale; 1505 if (needInvalidate) { 1506 // ensure {@link #webkitDraw} is called as we were blocking in 1507 // {@link #contentDraw} when mCurrentViewWidth is 0 1508 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged"); 1509 contentDraw(); 1510 } 1511 mEventHub.sendMessage(Message.obtain(null, 1512 EventHub.UPDATE_CACHE_AND_TEXT_ENTRY)); 1513 } 1514 1515 private void sendUpdateTextEntry() { 1516 if (mWebView != null) { 1517 Message.obtain(mWebView.mPrivateHandler, 1518 WebView.UPDATE_TEXT_ENTRY_MSG_ID).sendToTarget(); 1519 } 1520 } 1521 1522 // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize 1523 // callbacks. Computes the sum of database quota for all origins. 1524 private long getUsedQuota() { 1525 WebStorage webStorage = WebStorage.getInstance(); 1526 Collection<WebStorage.Origin> origins = webStorage.getOriginsSync(); 1527 1528 if (origins == null) { 1529 return 0; 1530 } 1531 long usedQuota = 0; 1532 for (WebStorage.Origin website : origins) { 1533 usedQuota += website.getQuota(); 1534 } 1535 return usedQuota; 1536 } 1537 1538 // Used to avoid posting more than one draw message. 1539 private boolean mDrawIsScheduled; 1540 1541 // Used to avoid posting more than one split picture message. 1542 private boolean mSplitPictureIsScheduled; 1543 1544 // Used to suspend drawing. 1545 private boolean mDrawIsPaused; 1546 1547 // mRestoreState is set in didFirstLayout(), and reset in the next 1548 // webkitDraw after passing it to the UI thread. 1549 private RestoreState mRestoreState = null; 1550 1551 static class RestoreState { 1552 float mMinScale; 1553 float mMaxScale; 1554 float mViewScale; 1555 float mTextWrapScale; 1556 float mDefaultScale; 1557 int mScrollX; 1558 int mScrollY; 1559 boolean mMobileSite; 1560 } 1561 1562 static class DrawData { 1563 DrawData() { 1564 mInvalRegion = new Region(); 1565 mWidthHeight = new Point(); 1566 } 1567 Region mInvalRegion; 1568 Point mViewPoint; 1569 Point mWidthHeight; 1570 int mMinPrefWidth; 1571 RestoreState mRestoreState; // only non-null if it is for the first 1572 // picture set after the first layout 1573 } 1574 1575 private void webkitDraw() { 1576 mDrawIsScheduled = false; 1577 DrawData draw = new DrawData(); 1578 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start"); 1579 if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight) 1580 == false) { 1581 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort"); 1582 return; 1583 } 1584 if (mWebView != null) { 1585 // Send the native view size that was used during the most recent 1586 // layout. 1587 draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight); 1588 if (mSettings.getUseWideViewPort()) { 1589 draw.mMinPrefWidth = Math.max( 1590 mViewportWidth == -1 ? DEFAULT_VIEWPORT_WIDTH 1591 : (mViewportWidth == 0 ? mCurrentViewWidth 1592 : mViewportWidth), 1593 nativeGetContentMinPrefWidth()); 1594 } 1595 if (mRestoreState != null) { 1596 draw.mRestoreState = mRestoreState; 1597 mRestoreState = null; 1598 } 1599 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); 1600 Message.obtain(mWebView.mPrivateHandler, 1601 WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); 1602 if (mWebkitScrollX != 0 || mWebkitScrollY != 0) { 1603 // as we have the new picture, try to sync the scroll position 1604 Message.obtain(mWebView.mPrivateHandler, 1605 WebView.SYNC_SCROLL_TO_MSG_ID, mWebkitScrollX, 1606 mWebkitScrollY).sendToTarget(); 1607 mWebkitScrollX = mWebkitScrollY = 0; 1608 } 1609 } 1610 } 1611 1612 /////////////////////////////////////////////////////////////////////////// 1613 // These are called from the UI thread, not our thread 1614 1615 static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG | 1616 Paint.DITHER_FLAG | 1617 Paint.SUBPIXEL_TEXT_FLAG; 1618 static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG | 1619 Paint.DITHER_FLAG; 1620 1621 final DrawFilter mZoomFilter = 1622 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG); 1623 final DrawFilter mScrollFilter = 1624 new PaintFlagsDrawFilter(SCROLL_BITS, 0); 1625 1626 /* package */ void drawContentPicture(Canvas canvas, int color, 1627 boolean animatingZoom, 1628 boolean animatingScroll) { 1629 DrawFilter df = null; 1630 if (animatingZoom) { 1631 df = mZoomFilter; 1632 } else if (animatingScroll) { 1633 df = mScrollFilter; 1634 } 1635 canvas.setDrawFilter(df); 1636 boolean tookTooLong = nativeDrawContent(canvas, color); 1637 canvas.setDrawFilter(null); 1638 if (tookTooLong && mSplitPictureIsScheduled == false) { 1639 mSplitPictureIsScheduled = true; 1640 sendMessage(EventHub.SPLIT_PICTURE_SET); 1641 } 1642 } 1643 1644 /* package */ synchronized boolean pictureReady() { 1645 return nativePictureReady(); 1646 } 1647 1648 /*package*/ synchronized Picture copyContentPicture() { 1649 Picture result = new Picture(); 1650 nativeCopyContentToPicture(result); 1651 return result; 1652 } 1653 1654 static void pauseUpdate(WebViewCore core) { 1655 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1656 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1657 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1658 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1659 .obtainMessage(WebCoreThread.REDUCE_PRIORITY)); 1660 // Note: there is one possible failure mode. If pauseUpdate() is called 1661 // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out 1662 // of the queue and about to be executed. mDrawIsScheduled may be set to 1663 // false in webkitDraw(). So update won't be blocked. But at least the 1664 // webcore thread priority is still lowered. 1665 if (core != null) { 1666 synchronized (core) { 1667 core.mDrawIsPaused = true; 1668 core.mEventHub.removeMessages(EventHub.WEBKIT_DRAW); 1669 } 1670 } 1671 } 1672 1673 static void resumeUpdate(WebViewCore core) { 1674 // remove the pending REDUCE_PRIORITY and RESUME_PRIORITY messages 1675 sWebCoreHandler.removeMessages(WebCoreThread.REDUCE_PRIORITY); 1676 sWebCoreHandler.removeMessages(WebCoreThread.RESUME_PRIORITY); 1677 sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler 1678 .obtainMessage(WebCoreThread.RESUME_PRIORITY)); 1679 if (core != null) { 1680 synchronized (core) { 1681 core.mDrawIsScheduled = false; 1682 core.mDrawIsPaused = false; 1683 if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate"); 1684 core.contentDraw(); 1685 } 1686 } 1687 } 1688 1689 static void startCacheTransaction() { 1690 sWebCoreHandler.sendMessage(sWebCoreHandler 1691 .obtainMessage(WebCoreThread.RESUME_CACHE_TICKER)); 1692 } 1693 1694 static void endCacheTransaction() { 1695 sWebCoreHandler.sendMessage(sWebCoreHandler 1696 .obtainMessage(WebCoreThread.BLOCK_CACHE_TICKER)); 1697 } 1698 1699 ////////////////////////////////////////////////////////////////////////// 1700 1701 private void restoreState(int index) { 1702 WebBackForwardList list = mCallbackProxy.getBackForwardList(); 1703 int size = list.getSize(); 1704 for (int i = 0; i < size; i++) { 1705 list.getItemAtIndex(i).inflate(mBrowserFrame.mNativeFrame); 1706 } 1707 mBrowserFrame.mLoadInitFromJava = true; 1708 list.restoreIndex(mBrowserFrame.mNativeFrame, index); 1709 mBrowserFrame.mLoadInitFromJava = false; 1710 } 1711 1712 //------------------------------------------------------------------------- 1713 // Implement abstract methods in WebViewCore, native WebKit callback part 1714 //------------------------------------------------------------------------- 1715 1716 // called from JNI or WebView thread 1717 /* package */ void contentDraw() { 1718 // don't update the Picture until we have an initial width and finish 1719 // the first layout 1720 if (mCurrentViewWidth == 0 || !mBrowserFrame.firstLayoutDone()) { 1721 return; 1722 } 1723 // only fire an event if this is our first request 1724 synchronized (this) { 1725 if (mDrawIsPaused || mDrawIsScheduled) { 1726 return; 1727 } 1728 mDrawIsScheduled = true; 1729 mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); 1730 } 1731 } 1732 1733 // called by JNI 1734 private void contentScrollBy(int dx, int dy, boolean animate) { 1735 if (!mBrowserFrame.firstLayoutDone()) { 1736 // Will this happen? If yes, we need to do something here. 1737 return; 1738 } 1739 if (mWebView != null) { 1740 Message msg = Message.obtain(mWebView.mPrivateHandler, 1741 WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate)); 1742 if (mDrawIsScheduled) { 1743 mEventHub.sendMessage(Message.obtain(null, 1744 EventHub.MESSAGE_RELAY, msg)); 1745 } else { 1746 msg.sendToTarget(); 1747 } 1748 } 1749 } 1750 1751 // called by JNI 1752 private void contentScrollTo(int x, int y) { 1753 if (!mBrowserFrame.firstLayoutDone()) { 1754 /* 1755 * WebKit restore state will be called before didFirstLayout(), 1756 * remember the position as it has to be applied after restoring 1757 * zoom factor which is controlled by screenWidth. 1758 */ 1759 mRestoredX = x; 1760 mRestoredY = y; 1761 return; 1762 } 1763 if (mWebView != null) { 1764 Message msg = Message.obtain(mWebView.mPrivateHandler, 1765 WebView.SCROLL_TO_MSG_ID, x, y); 1766 if (mDrawIsScheduled) { 1767 mEventHub.sendMessage(Message.obtain(null, 1768 EventHub.MESSAGE_RELAY, msg)); 1769 } else { 1770 msg.sendToTarget(); 1771 } 1772 } 1773 } 1774 1775 // called by JNI 1776 private void contentSpawnScrollTo(int x, int y) { 1777 if (!mBrowserFrame.firstLayoutDone()) { 1778 /* 1779 * WebKit restore state will be called before didFirstLayout(), 1780 * remember the position as it has to be applied after restoring 1781 * zoom factor which is controlled by screenWidth. 1782 */ 1783 mRestoredX = x; 1784 mRestoredY = y; 1785 return; 1786 } 1787 if (mWebView != null) { 1788 Message msg = Message.obtain(mWebView.mPrivateHandler, 1789 WebView.SPAWN_SCROLL_TO_MSG_ID, x, y); 1790 if (mDrawIsScheduled) { 1791 mEventHub.sendMessage(Message.obtain(null, 1792 EventHub.MESSAGE_RELAY, msg)); 1793 } else { 1794 msg.sendToTarget(); 1795 } 1796 } 1797 } 1798 1799 // called by JNI 1800 private void sendNotifyProgressFinished() { 1801 sendUpdateTextEntry(); 1802 // as CacheManager can behave based on database transaction, we need to 1803 // call tick() to trigger endTransaction 1804 sWebCoreHandler.removeMessages(WebCoreThread.CACHE_TICKER); 1805 sWebCoreHandler.sendMessage(sWebCoreHandler 1806 .obtainMessage(WebCoreThread.CACHE_TICKER)); 1807 contentDraw(); 1808 } 1809 1810 /* Called by JNI. The coordinates are in doc coordinates, so they need to 1811 be scaled before they can be used by the view system, which happens 1812 in WebView since it (and its thread) know the current scale factor. 1813 */ 1814 private void sendViewInvalidate(int left, int top, int right, int bottom) { 1815 if (mWebView != null) { 1816 Message.obtain(mWebView.mPrivateHandler, 1817 WebView.INVAL_RECT_MSG_ID, 1818 new Rect(left, top, right, bottom)).sendToTarget(); 1819 } 1820 } 1821 1822 /* package */ WebView getWebView() { 1823 return mWebView; 1824 } 1825 1826 private native void setViewportSettingsFromNative(); 1827 1828 // called by JNI 1829 private void didFirstLayout(boolean standardLoad) { 1830 if (DebugFlags.WEB_VIEW_CORE) { 1831 Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad); 1832 } 1833 1834 mBrowserFrame.didFirstLayout(); 1835 1836 if (mWebView == null) return; 1837 1838 setupViewport(standardLoad || mRestoredScale > 0); 1839 1840 // reset the scroll position, the restored offset and scales 1841 mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY 1842 = mRestoredScale = mRestoredScreenWidthScale = 0; 1843 } 1844 1845 // called by JNI 1846 private void updateViewport() { 1847 // if updateViewport is called before first layout, wait until first 1848 // layout to update the viewport. In the rare case, this is called after 1849 // first layout, force an update as we have just parsed the viewport 1850 // meta tag. 1851 if (mBrowserFrame.firstLayoutDone()) { 1852 setupViewport(true); 1853 } 1854 } 1855 1856 private void setupViewport(boolean updateRestoreState) { 1857 // set the viewport settings from WebKit 1858 setViewportSettingsFromNative(); 1859 1860 // adjust the default scale to match the densityDpi 1861 float adjust = 1.0f; 1862 if (mViewportDensityDpi == -1) { 1863 if (WebView.DEFAULT_SCALE_PERCENT != 100) { 1864 adjust = WebView.DEFAULT_SCALE_PERCENT / 100.0f; 1865 } 1866 } else if (mViewportDensityDpi > 0) { 1867 adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi 1868 / mViewportDensityDpi; 1869 } 1870 int defaultScale = (int) (adjust * 100); 1871 1872 if (mViewportInitialScale > 0) { 1873 mViewportInitialScale *= adjust; 1874 } 1875 if (mViewportMinimumScale > 0) { 1876 mViewportMinimumScale *= adjust; 1877 } 1878 if (mViewportMaximumScale > 0) { 1879 mViewportMaximumScale *= adjust; 1880 } 1881 1882 // infer the values if they are not defined. 1883 if (mViewportWidth == 0) { 1884 if (mViewportInitialScale == 0) { 1885 mViewportInitialScale = defaultScale; 1886 } 1887 } 1888 if (mViewportUserScalable == false) { 1889 mViewportInitialScale = defaultScale; 1890 mViewportMinimumScale = defaultScale; 1891 mViewportMaximumScale = defaultScale; 1892 } 1893 if (mViewportMinimumScale > mViewportInitialScale 1894 && mViewportInitialScale != 0) { 1895 mViewportMinimumScale = mViewportInitialScale; 1896 } 1897 if (mViewportMaximumScale > 0 1898 && mViewportMaximumScale < mViewportInitialScale) { 1899 mViewportMaximumScale = mViewportInitialScale; 1900 } 1901 if (mViewportWidth < 0 && mViewportInitialScale == defaultScale) { 1902 mViewportWidth = 0; 1903 } 1904 1905 // if mViewportWidth is 0, it means device-width, always update. 1906 if (mViewportWidth != 0 && !updateRestoreState) return; 1907 1908 // now notify webview 1909 // webViewWidth refers to the width in the view system 1910 int webViewWidth; 1911 // viewportWidth refers to the width in the document system 1912 int viewportWidth = mCurrentViewWidth; 1913 if (viewportWidth == 0) { 1914 // this may happen when WebView just starts. This is not perfect as 1915 // we call WebView method from WebCore thread. But not perfect 1916 // reference is better than no reference. 1917 webViewWidth = mWebView.getViewWidth(); 1918 viewportWidth = (int) (webViewWidth / adjust); 1919 if (viewportWidth == 0) { 1920 Log.w(LOGTAG, "Can't get the viewWidth after the first layout"); 1921 } 1922 } else { 1923 webViewWidth = Math.round(viewportWidth * mCurrentViewScale); 1924 } 1925 mRestoreState = new RestoreState(); 1926 mRestoreState.mMinScale = mViewportMinimumScale / 100.0f; 1927 mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f; 1928 mRestoreState.mDefaultScale = adjust; 1929 mRestoreState.mScrollX = mRestoredX; 1930 mRestoreState.mScrollY = mRestoredY; 1931 mRestoreState.mMobileSite = (0 == mViewportWidth); 1932 if (mRestoredScale > 0) { 1933 if (mRestoredScreenWidthScale > 0) { 1934 mRestoreState.mTextWrapScale = 1935 mRestoredScreenWidthScale / 100.0f; 1936 // 0 will trigger WebView to turn on zoom overview mode 1937 mRestoreState.mViewScale = 0; 1938 } else { 1939 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 1940 mRestoredScale / 100.0f; 1941 } 1942 } else { 1943 if (mViewportInitialScale > 0) { 1944 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 1945 mViewportInitialScale / 100.0f; 1946 } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) { 1947 mRestoreState.mViewScale = mRestoreState.mTextWrapScale = 1948 (float) webViewWidth / mViewportWidth; 1949 } else { 1950 mRestoreState.mTextWrapScale = adjust; 1951 // 0 will trigger WebView to turn on zoom overview mode 1952 mRestoreState.mViewScale = 0; 1953 } 1954 } 1955 1956 if (mWebView.mHeightCanMeasure) { 1957 // Trick to ensure that the Picture has the exact height for the 1958 // content by forcing to layout with 0 height after the page is 1959 // ready, which is indicated by didFirstLayout. This is essential to 1960 // get rid of the white space in the GMail which uses WebView for 1961 // message view. 1962 mWebView.mLastHeightSent = 0; 1963 // Send a negative scale to indicate that WebCore should reuse 1964 // the current scale 1965 WebView.ViewSizeData data = new WebView.ViewSizeData(); 1966 data.mWidth = mWebView.mLastWidthSent; 1967 data.mHeight = 0; 1968 // if mHeightCanMeasure is true, getUseWideViewPort() can't be 1969 // true. It is safe to use mWidth for mTextWrapWidth. 1970 data.mTextWrapWidth = data.mWidth; 1971 data.mScale = -1.0f; 1972 data.mIgnoreHeight = false; 1973 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 1974 EventHub.VIEW_SIZE_CHANGED, data)); 1975 } else if (mSettings.getUseWideViewPort()) { 1976 if (viewportWidth == 0) { 1977 // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView 1978 // to WebViewCore 1979 mWebView.mLastWidthSent = 0; 1980 } else { 1981 WebView.ViewSizeData data = new WebView.ViewSizeData(); 1982 // mViewScale as 0 means it is in zoom overview mode. So we don't 1983 // know the exact scale. If mRestoredScale is non-zero, use it; 1984 // otherwise just use mTextWrapScale as the initial scale. 1985 data.mScale = mRestoreState.mViewScale == 0 1986 ? (mRestoredScale > 0 ? mRestoredScale 1987 : mRestoreState.mTextWrapScale) 1988 : mRestoreState.mViewScale; 1989 data.mWidth = Math.round(webViewWidth / data.mScale); 1990 data.mHeight = mCurrentViewHeight * data.mWidth / viewportWidth; 1991 data.mTextWrapWidth = Math.round(webViewWidth 1992 / mRestoreState.mTextWrapScale); 1993 data.mIgnoreHeight = false; 1994 mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, 1995 EventHub.VIEW_SIZE_CHANGED, data)); 1996 } 1997 } 1998 } 1999 2000 // called by JNI 2001 private void restoreScale(int scale) { 2002 if (mBrowserFrame.firstLayoutDone() == false) { 2003 mRestoredScale = scale; 2004 } 2005 } 2006 2007 // called by JNI 2008 private void restoreScreenWidthScale(int scale) { 2009 if (!mSettings.getUseWideViewPort()) { 2010 return; 2011 } 2012 2013 if (mBrowserFrame.firstLayoutDone() == false) { 2014 mRestoredScreenWidthScale = scale; 2015 } 2016 } 2017 2018 // called by JNI 2019 private void needTouchEvents(boolean need) { 2020 if (mWebView != null) { 2021 Message.obtain(mWebView.mPrivateHandler, 2022 WebView.WEBCORE_NEED_TOUCH_EVENTS, need ? 1 : 0, 0) 2023 .sendToTarget(); 2024 } 2025 } 2026 2027 // called by JNI 2028 private void updateTextfield(int ptr, boolean changeToPassword, 2029 String text, int textGeneration) { 2030 if (mWebView != null) { 2031 Message msg = Message.obtain(mWebView.mPrivateHandler, 2032 WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr, 2033 textGeneration, text); 2034 msg.getData().putBoolean("password", changeToPassword); 2035 msg.sendToTarget(); 2036 } 2037 } 2038 2039 // called by JNI 2040 private void updateTextSelection(int pointer, int start, int end, 2041 int textGeneration) { 2042 if (mWebView != null) { 2043 Message.obtain(mWebView.mPrivateHandler, 2044 WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration, 2045 new TextSelectionData(start, end)).sendToTarget(); 2046 } 2047 } 2048 2049 // called by JNI 2050 private void clearTextEntry() { 2051 if (mWebView == null) return; 2052 Message.obtain(mWebView.mPrivateHandler, 2053 WebView.CLEAR_TEXT_ENTRY).sendToTarget(); 2054 } 2055 2056 private native void nativeUpdateFrameCacheIfLoading(); 2057 2058 /** 2059 * Scroll the focused textfield to (x, y) in document space 2060 */ 2061 private native void nativeScrollFocusedTextInput(int x, int y); 2062 2063 // these must be in document space (i.e. not scaled/zoomed). 2064 private native void nativeSetScrollOffset(int gen, int dx, int dy); 2065 2066 private native void nativeSetGlobalBounds(int x, int y, int w, int h); 2067 2068 // called by JNI 2069 private void requestListBox(String[] array, boolean[] enabledArray, 2070 int[] selectedArray) { 2071 if (mWebView != null) { 2072 mWebView.requestListBox(array, enabledArray, selectedArray); 2073 } 2074 } 2075 2076 // called by JNI 2077 private void requestListBox(String[] array, boolean[] enabledArray, 2078 int selection) { 2079 if (mWebView != null) { 2080 mWebView.requestListBox(array, enabledArray, selection); 2081 } 2082 2083 } 2084 2085 // called by JNI 2086 private void requestKeyboard(boolean showKeyboard) { 2087 if (mWebView != null) { 2088 Message.obtain(mWebView.mPrivateHandler, 2089 WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0) 2090 .sendToTarget(); 2091 } 2092 } 2093 2094 // PluginWidget functions for creating SurfaceViews for the Surface drawing 2095 // model. 2096 private ViewManager.ChildView createSurface(String packageName, String className, 2097 int npp, int x, int y, int width, int height) { 2098 if (mWebView == null) { 2099 return null; 2100 } 2101 PluginStub stub = PluginUtil.getPluginStub(mWebView.getContext(), packageName, className); 2102 if (stub == null) { 2103 Log.e(LOGTAG, "Unable to find plugin class (" + className + 2104 ") in the apk (" + packageName + ")"); 2105 return null; 2106 } 2107 2108 View pluginView = stub.getEmbeddedView(npp, mWebView.getContext()); 2109 2110 ViewManager.ChildView view = mWebView.mViewManager.createView(); 2111 view.mView = pluginView; 2112 view.attachView(x, y, width, height); 2113 return view; 2114 } 2115 2116 private void destroySurface(ViewManager.ChildView childView) { 2117 childView.removeView(); 2118 } 2119 2120 private native void nativePause(); 2121 private native void nativeResume(); 2122 private native void nativeFreeMemory(); 2123} 2124